Author: hselasky
Date: Wed May 31 13:05:54 2017
New Revision: 319319
URL: https://svnweb.freebsd.org/changeset/base/319319

Log:
  Remove the VMA handle from its list before calling the LinuxKPI VMA
  close operation to prevent other threads from reusing the VM object
  handle pointer.
  
  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  Wed May 31 13:01:27 
2017        (r319318)
+++ head/sys/compat/linuxkpi/common/src/linux_compat.c  Wed May 31 13:05:54 
2017        (r319319)
@@ -486,6 +486,15 @@ static struct rwlock linux_vma_lock;
 static TAILQ_HEAD(, vm_area_struct) linux_vma_head =
     TAILQ_HEAD_INITIALIZER(linux_vma_head);
 
+static void
+linux_cdev_handle_free(struct vm_area_struct *vmap)
+{
+       /* Drop reference on mm_struct */
+       mmput(vmap->vm_mm);
+
+       kfree(vmap);
+}
+
 static struct vm_area_struct *
 linux_cdev_handle_insert(void *handle, struct vm_area_struct *vmap)
 {
@@ -495,20 +504,10 @@ linux_cdev_handle_insert(void *handle, struct vm_area_
        TAILQ_FOREACH(ptr, &linux_vma_head, vm_entry) {
                if (ptr->vm_private_data == handle) {
                        rw_wunlock(&linux_vma_lock);
-                       kfree(vmap);
+                       linux_cdev_handle_free(vmap);
                        return (NULL);
                }
        }
-       /*
-        * The same VM object might be shared by multiple processes
-        * and the mm_struct is usually freed when a process exits.
-        *
-        * The atomic reference below makes sure the mm_struct is
-        * available as long as the vmap is in the linux_vma_head.
-        */
-       if (atomic_inc_not_zero(&vmap->vm_mm->mm_users) == 0)
-               panic("linuxkpi: mm_users is zero\n");
-
        TAILQ_INSERT_TAIL(&linux_vma_head, vmap, vm_entry);
        rw_wunlock(&linux_vma_lock);
        return (vmap);
@@ -517,16 +516,9 @@ linux_cdev_handle_insert(void *handle, struct vm_area_
 static void
 linux_cdev_handle_remove(struct vm_area_struct *vmap)
 {
-       if (vmap == NULL)
-               return;
-
        rw_wlock(&linux_vma_lock);
        TAILQ_REMOVE(&linux_vma_head, vmap, vm_entry);
        rw_wunlock(&linux_vma_lock);
-
-       /* Drop reference on mm_struct */
-       mmput(vmap->vm_mm);
-       kfree(vmap);
 }
 
 static struct vm_area_struct *
@@ -562,13 +554,19 @@ linux_cdev_pager_dtor(void *handle)
        vmap = linux_cdev_handle_find(handle);
        MPASS(vmap != NULL);
 
+       /*
+        * Remove handle before calling close operation to prevent
+        * other threads from reusing the handle pointer.
+        */
+       linux_cdev_handle_remove(vmap);
+
        down_write(&vmap->vm_mm->mmap_sem);
        vm_ops = vmap->vm_ops;
        if (likely(vm_ops != NULL))
                vm_ops->close(vmap);
        up_write(&vmap->vm_mm->mmap_sem);
 
-       linux_cdev_handle_remove(vmap);
+       linux_cdev_handle_free(vmap);
 }
 
 static struct cdev_pager_ops linux_cdev_pager_ops = {
@@ -895,6 +893,7 @@ linux_dev_mmap_single(struct cdev *dev, vm_ooffset_t *
     vm_size_t size, struct vm_object **object, int nprot)
 {
        struct vm_area_struct *vmap;
+       struct mm_struct *mm;
        struct linux_file *filp;
        struct thread *td;
        struct file *file;
@@ -914,6 +913,17 @@ linux_dev_mmap_single(struct cdev *dev, vm_ooffset_t *
 
        linux_set_current(td);
 
+       /*
+        * The same VM object might be shared by multiple processes
+        * and the mm_struct is usually freed when a process exits.
+        *
+        * The atomic reference below makes sure the mm_struct is
+        * available as long as the vmap is in the linux_vma_head.
+        */
+       mm = current->mm;
+       if (atomic_inc_not_zero(&mm->mm_users) == 0)
+               return (EINVAL);
+
        vmap = kzalloc(sizeof(*vmap), GFP_KERNEL);
        vmap->vm_start = 0;
        vmap->vm_end = size;
@@ -922,7 +932,7 @@ linux_dev_mmap_single(struct cdev *dev, vm_ooffset_t *
        vmap->vm_flags = vmap->vm_page_prot = nprot;
        vmap->vm_ops = NULL;
        vmap->vm_file = filp;
-       vmap->vm_mm = current->mm;
+       vmap->vm_mm = mm;
 
        if (unlikely(down_write_killable(&vmap->vm_mm->mmap_sem))) {
                error = EINTR;
@@ -932,7 +942,7 @@ linux_dev_mmap_single(struct cdev *dev, vm_ooffset_t *
        }
 
        if (error != 0) {
-               kfree(vmap);
+               linux_cdev_handle_free(vmap);
                return (error);
        }
 
@@ -945,7 +955,7 @@ linux_dev_mmap_single(struct cdev *dev, vm_ooffset_t *
                    vmap->vm_ops->open == NULL ||
                    vmap->vm_ops->close == NULL ||
                    vmap->vm_private_data == NULL) {
-                       kfree(vmap);
+                       linux_cdev_handle_free(vmap);
                        return (EINVAL);
                }
 
@@ -958,6 +968,7 @@ linux_dev_mmap_single(struct cdev *dev, vm_ooffset_t *
 
                if (*object == NULL) {
                        linux_cdev_handle_remove(vmap);
+                       linux_cdev_handle_free(vmap);
                        return (EINVAL);
                }
        } else {
@@ -969,7 +980,7 @@ linux_dev_mmap_single(struct cdev *dev, vm_ooffset_t *
                *object = vm_pager_allocate(OBJT_SG, sg, vmap->vm_len,
                    nprot, 0, curthread->td_ucred);
 
-               kfree(vmap);
+               linux_cdev_handle_free(vmap);
 
                if (*object == NULL) {
                        sglist_free(sg);
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to