Group parameter check logic together, moving check_mremap_params() next to
it.

This puts all such checks into a single place, and invokes them early so we
can simply bail out as soon as we are aware that a condition is not met.

No functional change intended.

Signed-off-by: Lorenzo Stoakes <lorenzo.stoa...@oracle.com>
Reviewed-by: Vlastimil Babka <vba...@suse.cz>
---
 mm/mremap.c | 273 +++++++++++++++++++++++++---------------------------
 1 file changed, 131 insertions(+), 142 deletions(-)

diff --git a/mm/mremap.c b/mm/mremap.c
index 116203766ce0..3f8daa3314f0 100644
--- a/mm/mremap.c
+++ b/mm/mremap.c
@@ -1308,64 +1308,6 @@ static unsigned long move_vma(struct vma_remap_struct 
*vrm)
        return err ? (unsigned long)err : vrm->new_addr;
 }
 
-/*
- * remap_is_valid() - Ensure the VMA can be moved or resized to the new length,
- * at the given address.
- *
- * Return 0 on success, error otherwise.
- */
-static int remap_is_valid(struct vma_remap_struct *vrm)
-{
-       struct mm_struct *mm = current->mm;
-       struct vm_area_struct *vma = vrm->vma;
-       unsigned long addr = vrm->addr;
-       unsigned long old_len = vrm->old_len;
-       unsigned long new_len = vrm->new_len;
-       unsigned long pgoff;
-
-       /*
-        * !old_len is a special case where an attempt is made to 'duplicate'
-        * a mapping.  This makes no sense for private mappings as it will
-        * instead create a fresh/new mapping unrelated to the original.  This
-        * is contrary to the basic idea of mremap which creates new mappings
-        * based on the original.  There are no known use cases for this
-        * behavior.  As a result, fail such attempts.
-        */
-       if (!old_len && !(vma->vm_flags & (VM_SHARED | VM_MAYSHARE))) {
-               pr_warn_once("%s (%d): attempted to duplicate a private mapping 
with mremap.  This is not supported.\n",
-                            current->comm, current->pid);
-               return -EINVAL;
-       }
-
-       if ((vrm->flags & MREMAP_DONTUNMAP) &&
-                       (vma->vm_flags & (VM_DONTEXPAND | VM_PFNMAP)))
-               return -EINVAL;
-
-       /* We can't remap across vm area boundaries */
-       if (old_len > vma->vm_end - addr)
-               return -EFAULT;
-
-       if (new_len <= old_len)
-               return 0;
-
-       /* Need to be careful about a growing mapping */
-       pgoff = (addr - vma->vm_start) >> PAGE_SHIFT;
-       pgoff += vma->vm_pgoff;
-       if (pgoff + (new_len >> PAGE_SHIFT) < pgoff)
-               return -EINVAL;
-
-       if (vma->vm_flags & (VM_DONTEXPAND | VM_PFNMAP))
-               return -EFAULT;
-
-       if (!mlock_future_ok(mm, vma->vm_flags, vrm->delta))
-               return -EAGAIN;
-
-       if (!may_expand_vm(mm, vma->vm_flags, vrm->delta >> PAGE_SHIFT))
-               return -ENOMEM;
-
-       return 0;
-}
-
 /*
  * The user has requested that the VMA be shrunk (i.e., old_len > new_len), so
  * execute this, optionally dropping the mmap lock when we do so.
@@ -1492,77 +1434,6 @@ static bool vrm_can_expand_in_place(struct 
vma_remap_struct *vrm)
        return true;
 }
 
-/*
- * Are the parameters passed to mremap() valid? If so return 0, otherwise 
return
- * error.
- */
-static unsigned long check_mremap_params(struct vma_remap_struct *vrm)
-
-{
-       unsigned long addr = vrm->addr;
-       unsigned long flags = vrm->flags;
-
-       /* Ensure no unexpected flag values. */
-       if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP))
-               return -EINVAL;
-
-       /* Start address must be page-aligned. */
-       if (offset_in_page(addr))
-               return -EINVAL;
-
-       /*
-        * We allow a zero old-len as a special case
-        * for DOS-emu "duplicate shm area" thing. But
-        * a zero new-len is nonsensical.
-        */
-       if (!vrm->new_len)
-               return -EINVAL;
-
-       /* Is the new length or address silly? */
-       if (vrm->new_len > TASK_SIZE ||
-           vrm->new_addr > TASK_SIZE - vrm->new_len)
-               return -EINVAL;
-
-       /* Remainder of checks are for cases with specific new_addr. */
-       if (!vrm_implies_new_addr(vrm))
-               return 0;
-
-       /* The new address must be page-aligned. */
-       if (offset_in_page(vrm->new_addr))
-               return -EINVAL;
-
-       /* A fixed address implies a move. */
-       if (!(flags & MREMAP_MAYMOVE))
-               return -EINVAL;
-
-       /* MREMAP_DONTUNMAP does not allow resizing in the process. */
-       if (flags & MREMAP_DONTUNMAP && vrm->old_len != vrm->new_len)
-               return -EINVAL;
-
-       /* Target VMA must not overlap source VMA. */
-       if (vrm_overlaps(vrm))
-               return -EINVAL;
-
-       /*
-        * move_vma() need us to stay 4 maps below the threshold, otherwise
-        * it will bail out at the very beginning.
-        * That is a problem if we have already unmaped the regions here
-        * (new_addr, and old_addr), because userspace will not know the
-        * state of the vma's after it gets -ENOMEM.
-        * So, to avoid such scenario we can pre-compute if the whole
-        * operation has high chances to success map-wise.
-        * Worst-scenario case is when both vma's (new_addr and old_addr) get
-        * split in 3 before unmapping it.
-        * That means 2 more maps (1 for each) to the ones we already hold.
-        * Check whether current map count plus 2 still leads us to 4 maps below
-        * the threshold, otherwise return -ENOMEM here to be more safe.
-        */
-       if ((current->mm->map_count + 2) >= sysctl_max_map_count - 3)
-               return -ENOMEM;
-
-       return 0;
-}
-
 /*
  * We know we can expand the VMA in-place by delta pages, so do so.
  *
@@ -1714,9 +1585,26 @@ static bool vrm_will_map_new(struct vma_remap_struct 
*vrm)
        return false;
 }
 
+static void notify_uffd(struct vma_remap_struct *vrm, bool failed)
+{
+       struct mm_struct *mm = current->mm;
+
+       /* Regardless of success/failure, we always notify of any unmaps. */
+       userfaultfd_unmap_complete(mm, vrm->uf_unmap_early);
+       if (failed)
+               mremap_userfaultfd_fail(vrm->uf);
+       else
+               mremap_userfaultfd_complete(vrm->uf, vrm->addr,
+                       vrm->new_addr, vrm->old_len);
+       userfaultfd_unmap_complete(mm, vrm->uf_unmap);
+}
+
 static int check_prep_vma(struct vma_remap_struct *vrm)
 {
        struct vm_area_struct *vma = vrm->vma;
+       struct mm_struct *mm = current->mm;
+       unsigned long addr = vrm->addr;
+       unsigned long old_len, new_len, pgoff;
 
        if (!vma)
                return -EFAULT;
@@ -1733,26 +1621,127 @@ static int check_prep_vma(struct vma_remap_struct *vrm)
        vrm->remap_type = vrm_remap_type(vrm);
        /* For convenience, we set new_addr even if VMA won't move. */
        if (!vrm_implies_new_addr(vrm))
-               vrm->new_addr = vrm->addr;
+               vrm->new_addr = addr;
+
+       /* Below only meaningful if we expand or move a VMA. */
+       if (!vrm_will_map_new(vrm))
+               return 0;
 
-       if (vrm_will_map_new(vrm))
-               return remap_is_valid(vrm);
+       old_len = vrm->old_len;
+       new_len = vrm->new_len;
+
+       /*
+        * !old_len is a special case where an attempt is made to 'duplicate'
+        * a mapping.  This makes no sense for private mappings as it will
+        * instead create a fresh/new mapping unrelated to the original.  This
+        * is contrary to the basic idea of mremap which creates new mappings
+        * based on the original.  There are no known use cases for this
+        * behavior.  As a result, fail such attempts.
+        */
+       if (!old_len && !(vma->vm_flags & (VM_SHARED | VM_MAYSHARE))) {
+               pr_warn_once("%s (%d): attempted to duplicate a private mapping 
with mremap.  This is not supported.\n",
+                            current->comm, current->pid);
+               return -EINVAL;
+       }
+
+       if ((vrm->flags & MREMAP_DONTUNMAP) &&
+                       (vma->vm_flags & (VM_DONTEXPAND | VM_PFNMAP)))
+               return -EINVAL;
+
+       /* We can't remap across vm area boundaries */
+       if (old_len > vma->vm_end - addr)
+               return -EFAULT;
+
+       if (new_len <= old_len)
+               return 0;
+
+       /* Need to be careful about a growing mapping */
+       pgoff = (addr - vma->vm_start) >> PAGE_SHIFT;
+       pgoff += vma->vm_pgoff;
+       if (pgoff + (new_len >> PAGE_SHIFT) < pgoff)
+               return -EINVAL;
+
+       if (vma->vm_flags & (VM_DONTEXPAND | VM_PFNMAP))
+               return -EFAULT;
+
+       if (!mlock_future_ok(mm, vma->vm_flags, vrm->delta))
+               return -EAGAIN;
+
+       if (!may_expand_vm(mm, vma->vm_flags, vrm->delta >> PAGE_SHIFT))
+               return -ENOMEM;
 
        return 0;
 }
 
-static void notify_uffd(struct vma_remap_struct *vrm, bool failed)
+/*
+ * Are the parameters passed to mremap() valid? If so return 0, otherwise 
return
+ * error.
+ */
+static unsigned long check_mremap_params(struct vma_remap_struct *vrm)
+
 {
-       struct mm_struct *mm = current->mm;
+       unsigned long addr = vrm->addr;
+       unsigned long flags = vrm->flags;
 
-       /* Regardless of success/failure, we always notify of any unmaps. */
-       userfaultfd_unmap_complete(mm, vrm->uf_unmap_early);
-       if (failed)
-               mremap_userfaultfd_fail(vrm->uf);
-       else
-               mremap_userfaultfd_complete(vrm->uf, vrm->addr,
-                       vrm->new_addr, vrm->old_len);
-       userfaultfd_unmap_complete(mm, vrm->uf_unmap);
+       /* Ensure no unexpected flag values. */
+       if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE | MREMAP_DONTUNMAP))
+               return -EINVAL;
+
+       /* Start address must be page-aligned. */
+       if (offset_in_page(addr))
+               return -EINVAL;
+
+       /*
+        * We allow a zero old-len as a special case
+        * for DOS-emu "duplicate shm area" thing. But
+        * a zero new-len is nonsensical.
+        */
+       if (!vrm->new_len)
+               return -EINVAL;
+
+       /* Is the new length or address silly? */
+       if (vrm->new_len > TASK_SIZE ||
+           vrm->new_addr > TASK_SIZE - vrm->new_len)
+               return -EINVAL;
+
+       /* Remainder of checks are for cases with specific new_addr. */
+       if (!vrm_implies_new_addr(vrm))
+               return 0;
+
+       /* The new address must be page-aligned. */
+       if (offset_in_page(vrm->new_addr))
+               return -EINVAL;
+
+       /* A fixed address implies a move. */
+       if (!(flags & MREMAP_MAYMOVE))
+               return -EINVAL;
+
+       /* MREMAP_DONTUNMAP does not allow resizing in the process. */
+       if (flags & MREMAP_DONTUNMAP && vrm->old_len != vrm->new_len)
+               return -EINVAL;
+
+       /* Target VMA must not overlap source VMA. */
+       if (vrm_overlaps(vrm))
+               return -EINVAL;
+
+       /*
+        * move_vma() need us to stay 4 maps below the threshold, otherwise
+        * it will bail out at the very beginning.
+        * That is a problem if we have already unmaped the regions here
+        * (new_addr, and old_addr), because userspace will not know the
+        * state of the vma's after it gets -ENOMEM.
+        * So, to avoid such scenario we can pre-compute if the whole
+        * operation has high chances to success map-wise.
+        * Worst-scenario case is when both vma's (new_addr and old_addr) get
+        * split in 3 before unmapping it.
+        * That means 2 more maps (1 for each) to the ones we already hold.
+        * Check whether current map count plus 2 still leads us to 4 maps below
+        * the threshold, otherwise return -ENOMEM here to be more safe.
+        */
+       if ((current->mm->map_count + 2) >= sysctl_max_map_count - 3)
+               return -ENOMEM;
+
+       return 0;
 }
 
 static unsigned long do_mremap(struct vma_remap_struct *vrm)
-- 
2.50.0


Reply via email to