]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
mm/mmap: write-lock VMAs before merging, splitting or expanding them
authorSuren Baghdasaryan <surenb@google.com>
Sat, 11 Jun 2022 05:37:08 +0000 (22:37 -0700)
committerSuren Baghdasaryan <surenb@google.com>
Wed, 23 Nov 2022 02:09:45 +0000 (02:09 +0000)
Decisions about whether VMAs can be merged, split or expanded must be
made while VMAs are protected from the changes which can affect that
decision. For example, merge_vma uses vma->anon_vma in its decision
whether the VMA can be merged. Meanwhile, page fault handler changes
vma->anon_vma during COW operation.
Write-lock all VMAs which might be affected by a merge or split operation
before making decision how such operations should be performed.

Not sure if expansion really needs this, just being paranoid. Otherwise
mmap_region and vm_brk_flags might not locking.

Signed-off-by: Suren Baghdasaryan <surenb@google.com>
mm/mmap.c

index 5ffff953c57072979bed312ad6ffb4991498c66e..4d8ac523b303c2b03bf23e4b422f533cf29334e9 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1018,10 +1018,17 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm,
        if (vm_flags & VM_SPECIAL)
                return NULL;
 
+       if (prev)
+               vma_write_lock(prev);
        next = find_vma(mm, prev ? prev->vm_end : 0);
        mid = next;
-       if (next && next->vm_end == end)                /* cases 6, 7, 8 */
+       if (next)
+               vma_write_lock(next);
+       if (next && next->vm_end == end) {              /* cases 6, 7, 8 */
                next = find_vma(mm, next->vm_end);
+               if (next)
+                       vma_write_lock(next);
+       }
 
        /* verify some invariant that must be enforced by the caller */
        VM_WARN_ON(prev && addr <= prev->vm_start);
@@ -2202,6 +2209,7 @@ int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
        int err;
        validate_mm_mt(mm);
 
+       vma_write_lock(vma);
        if (vma->vm_ops && vma->vm_ops->may_split) {
                err = vma->vm_ops->may_split(vma, addr);
                if (err)
@@ -2567,6 +2575,8 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
 
        /* Attempt to expand an old mapping */
        /* Check next */
+       if (next)
+               vma_write_lock(next);
        if (next && next->vm_start == end && !vma_policy(next) &&
            can_vma_merge_before(next, vm_flags, NULL, file, pgoff+pglen,
                                 NULL_VM_UFFD_CTX, NULL)) {
@@ -2576,6 +2586,8 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
        }
 
        /* Check prev */
+       if (prev)
+               vma_write_lock(prev);
        if (prev && prev->vm_end == addr && !vma_policy(prev) &&
            (vma ? can_vma_merge_after(prev, vm_flags, vma->anon_vma, file,
                                       pgoff, vma->vm_userfaultfd_ctx, NULL) :
@@ -3038,6 +3050,8 @@ int vm_brk_flags(unsigned long addr, unsigned long request, unsigned long flags)
                goto munmap_failed;
 
        vma = mas_prev(&mas, 0);
+       if (vma)
+               vma_write_lock(vma);
        if (!vma || vma->vm_end != addr || vma_policy(vma) ||
            !can_vma_merge_after(vma, flags, NULL, NULL,
                                 addr >> PAGE_SHIFT, NULL_VM_UFFD_CTX, NULL))