From: Suren Baghdasaryan Date: Sat, 11 Jun 2022 05:37:08 +0000 (-0700) Subject: mm/mmap: write-lock VMAs before merging, splitting or expanding them X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=c46000458d573c6cb0ff57c969d27c7f689e57d8;p=users%2Fjedix%2Flinux-maple.git mm/mmap: write-lock VMAs before merging, splitting or expanding them 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 --- diff --git a/mm/mmap.c b/mm/mmap.c index 5ffff953c570..4d8ac523b303 100644 --- 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))