From 41b30362e99114d4d7ef753c612322471813ae39 Mon Sep 17 00:00:00 2001 From: "Liam R. Howlett" Date: Sat, 17 Jun 2023 20:47:08 -0400 Subject: [PATCH] BACKPORT: mm/mmap: Fix error path in do_vmi_align_munmap() commit 606c812eb1d5b5fb0dd9e330ca94b52d7c227830 upstream The error unrolling was leaving the VMAs detached in many cases and leaving the locked_vm statistic altered, and skipping the unrolling entirely in the case of the vma tree write failing. Fix the error path by re-attaching the detached VMAs and adding the necessary goto for the failed vma tree write, and fix the locked_vm statistic by only updating after the vma tree write succeeds. Fixes: 763ecb035029 ("mm: remove the vma linked list") Reported-by: Vegard Nossum Signed-off-by: Liam R. Howlett Signed-off-by: Linus Torvalds [ dwmw2: Strictly, the original patch wasn't *re-attaching* the detached VMAs. They *were* still attached but just had the 'detached' flag set, which is an optimisation. Which doesn't exist in 6.3, so drop that. Also drop the call to vma_start_write() which came in with the per-VMA locking in 6.4. ] [ dwmw2 (6.1): It's do_mas_align_munmap() here. And has two call sites for the now-removed munmap_sidetree() function. Inline them both rather then trying to backport various dependencies with potentially subtle interactions. ] Signed-off-by: David Woodhouse Signed-off-by: Greg Kroah-Hartman [surenb: added needed vma_start_write and vma_vma_mark_detached calls] Signed-off-by: Suren Baghdasaryan Change-Id: I1e42347ecf9eb46077739a267ac00264f94fa59a --- mm/mmap.c | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/mm/mmap.c b/mm/mmap.c index 36f129f23dfb..a0af03d35e8c 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2359,21 +2359,6 @@ int split_vma(struct mm_struct *mm, struct vm_area_struct *vma, return __split_vma(mm, vma, addr, new_below); } -static inline int munmap_sidetree(struct vm_area_struct *vma, int count, - struct ma_state *mas_detach) -{ - vma_start_write(vma); - mas_set(mas_detach, count); - if (mas_store_gfp(mas_detach, vma, GFP_KERNEL)) - return -ENOMEM; - - vma_mark_detached(vma, true); - if (vma->vm_flags & VM_LOCKED) - vma->vm_mm->locked_vm -= vma_pages(vma); - - return 0; -} - /* * do_mas_align_munmap() - munmap the aligned region from @start to @end. * @mas: The maple_state, ideally set up to alter the correct tree location. @@ -2395,6 +2380,7 @@ do_mas_align_munmap(struct ma_state *mas, struct vm_area_struct *vma, struct maple_tree mt_detach; int count = 0; int error = -ENOMEM; + unsigned long locked_vm = 0; MA_STATE(mas_detach, &mt_detach, 0, 0); mt_init_flags(&mt_detach, mas->tree->ma_flags & MT_FLAGS_LOCK_MASK); mt_set_external_lock(&mt_detach, &mm->mmap_lock); @@ -2450,18 +2436,27 @@ do_mas_align_munmap(struct ma_state *mas, struct vm_area_struct *vma, mas_set(mas, end); split = mas_prev(mas, 0); - error = munmap_sidetree(split, count, &mas_detach); + vma_start_write(split); + mas_set(&mas_detach, count); + error = mas_store_gfp(&mas_detach, split, GFP_KERNEL); if (error) - goto munmap_sidetree_failed; + goto munmap_gather_failed; + vma_mark_detached(split, true); + if (split->vm_flags & VM_LOCKED) + locked_vm += vma_pages(split); count++; if (vma == next) vma = split; break; } - error = munmap_sidetree(next, count, &mas_detach); - if (error) - goto munmap_sidetree_failed; + vma_start_write(next); + mas_set(&mas_detach, count); + if (mas_store_gfp(&mas_detach, next, GFP_KERNEL)) + goto munmap_gather_failed; + vma_mark_detached(next, true); + if (next->vm_flags & VM_LOCKED) + locked_vm += vma_pages(next); count++; if (unlikely(uf)) { @@ -2519,6 +2514,7 @@ do_mas_align_munmap(struct ma_state *mas, struct vm_area_struct *vma, if (mas_store_gfp(mas, NULL, GFP_KERNEL)) return -ENOMEM; + mm->locked_vm -= locked_vm; mm->map_count -= count; /* * Do not downgrade mmap_lock if we are next to VM_GROWSDOWN or @@ -2550,7 +2546,7 @@ do_mas_align_munmap(struct ma_state *mas, struct vm_area_struct *vma, return downgrade ? 1 : 0; userfaultfd_error: -munmap_sidetree_failed: +munmap_gather_failed: end_split_failed: __mt_destroy(&mt_detach); start_split_failed: