svn commit: r325767 - head/sys/compat/linuxkpi/common/src
Hans Petter Selasky
hselasky at FreeBSD.org
Mon Nov 13 18:16:28 UTC 2017
Author: hselasky
Date: Mon Nov 13 18:16:26 2017
New Revision: 325767
URL: https://svnweb.freebsd.org/changeset/base/325767
Log:
Properly handle the case where the linux_cdev_handle_insert() function
in the LinuxKPI returns NULL. This happens when the VM area's private
data handle already exists and could cause a so-called NULL pointer
dereferencing issue prior to this fix.
Found by: greg at unrelenting.technology
MFC after: 1 week
Sponsored by: Mellanox Technologies
Modified:
head/sys/compat/linuxkpi/common/src/linux_compat.c
Modified: head/sys/compat/linuxkpi/common/src/linux_compat.c
==============================================================================
--- head/sys/compat/linuxkpi/common/src/linux_compat.c Mon Nov 13 17:46:38 2017 (r325766)
+++ head/sys/compat/linuxkpi/common/src/linux_compat.c Mon Nov 13 18:16:26 2017 (r325767)
@@ -607,24 +607,6 @@ linux_cdev_handle_free(struct vm_area_struct *vmap)
kfree(vmap);
}
-static struct vm_area_struct *
-linux_cdev_handle_insert(void *handle, struct vm_area_struct *vmap)
-{
- struct vm_area_struct *ptr;
-
- rw_wlock(&linux_vma_lock);
- TAILQ_FOREACH(ptr, &linux_vma_head, vm_entry) {
- if (ptr->vm_private_data == handle) {
- rw_wunlock(&linux_vma_lock);
- linux_cdev_handle_free(vmap);
- return (NULL);
- }
- }
- TAILQ_INSERT_TAIL(&linux_vma_head, vmap, vm_entry);
- rw_wunlock(&linux_vma_lock);
- return (vmap);
-}
-
static void
linux_cdev_handle_remove(struct vm_area_struct *vmap)
{
@@ -1318,20 +1300,55 @@ linux_dev_mmap_single(struct cdev *dev, vm_ooffset_t *
attr = pgprot2cachemode(vmap->vm_page_prot);
if (vmap->vm_ops != NULL) {
+ struct vm_area_struct *ptr;
void *vm_private_data;
+ bool vm_no_fault;
if (vmap->vm_ops->open == NULL ||
vmap->vm_ops->close == NULL ||
vmap->vm_private_data == NULL) {
+ /* free allocated VM area struct */
linux_cdev_handle_free(vmap);
return (EINVAL);
}
vm_private_data = vmap->vm_private_data;
- vmap = linux_cdev_handle_insert(vm_private_data, vmap);
+ rw_wlock(&linux_vma_lock);
+ TAILQ_FOREACH(ptr, &linux_vma_head, vm_entry) {
+ if (ptr->vm_private_data == vm_private_data)
+ break;
+ }
+ /* check if there is an existing VM area struct */
+ if (ptr != NULL) {
+ /* check if the VM area structure is invalid */
+ if (ptr->vm_ops == NULL ||
+ ptr->vm_ops->open == NULL ||
+ ptr->vm_ops->close == NULL) {
+ error = ESTALE;
+ vm_no_fault = 1;
+ } else {
+ error = EEXIST;
+ vm_no_fault = (ptr->vm_ops->fault == NULL);
+ }
+ } else {
+ /* insert VM area structure into list */
+ TAILQ_INSERT_TAIL(&linux_vma_head, vmap, vm_entry);
+ error = 0;
+ vm_no_fault = (vmap->vm_ops->fault == NULL);
+ }
+ rw_wunlock(&linux_vma_lock);
- if (vmap->vm_ops->fault == NULL) {
+ if (error != 0) {
+ /* free allocated VM area struct */
+ linux_cdev_handle_free(vmap);
+ /* check for stale VM area struct */
+ if (error != EEXIST)
+ return (error);
+ }
+
+ /* check if there is no fault handler */
+ if (vm_no_fault) {
*object = cdev_pager_allocate(vm_private_data, OBJT_DEVICE,
&linux_cdev_pager_ops[1], size, nprot, *offset,
curthread->td_ucred);
@@ -1341,9 +1358,14 @@ linux_dev_mmap_single(struct cdev *dev, vm_ooffset_t *
curthread->td_ucred);
}
+ /* check if allocating the VM object failed */
if (*object == NULL) {
- linux_cdev_handle_remove(vmap);
- linux_cdev_handle_free(vmap);
+ if (error == 0) {
+ /* remove VM area struct from list */
+ linux_cdev_handle_remove(vmap);
+ /* free allocated VM area struct */
+ linux_cdev_handle_free(vmap);
+ }
return (EINVAL);
}
} else {
More information about the svn-src-head
mailing list