FAILED: patch "[PATCH] mm: unconditionally close VMAs on error" failed to apply to 5.10-stable tree
Subsystems (4)
[email protected]
maintainerThe patch below does not apply to the 5.10-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.10.y
git checkout FETCH_HEAD
git cherry-pick -x 4080ef1579b2413435413988d14ac8c68e4d42c8
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2024111157-muster-engaging-dad6@gregkh' --subject-prefix 'PATCH 5.10.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 4080ef1579b2413435413988d14ac8c68e4d42c8 Mon Sep 17 00:00:00 2001
From: Lorenzo Stoakes <[email protected]>
Date: Tue, 29 Oct 2024 18:11:45 +0000
Subject: [PATCH] mm: unconditionally close VMAs on error
Incorrect invocation of VMA callbacks when the VMA is no longer in a consistent state is bug prone and risky to perform.
With regards to the important vm_ops->close() callback We have gone to great lengths to try to track whether or not we ought to close VMAs.
Rather than doing so and risking making a mistake somewhere, instead unconditionally close and reset vma->vm_ops to an empty dummy operations set with a NULL .close operator.
We introduce a new function to do so - vma_close() - and simplify existing vms logic which tracked whether we needed to close or not.
This simplifies the logic, avoids incorrect double-calling of the .close() callback and allows us to update error paths to simply call vma_close() unconditionally - making VMA closure idempotent.
mm/internal.h +18 -0
@@ -135,6 +135,24 @@ static inline int mmap_file(struct file *file, struct vm_area_struct *vma)return err; } +/* +* If the VMA has a close hook then close it, and since closing it might leave +* it in an inconsistent state which makes the use of any hooks suspect, clear +* them down by installing dummy empty hooks. +*/ +static inline void vma_close (struct vm_area_struct *vma) +{ +if (vma->vm_ops && vma->vm_ops->close) { + vma->vm_ops->close (vma); + +/* +* The mapping is in an inconsistent state, and no further hooks +* may be invoked upon it. +*/ + vma->vm_ops = &vma_dummy_vm_ops; + } +} +#ifdef CONFIG_MMU /* Flags for folio_pte_batch(). */
mm/mmap.c +2 -3
@@ -1573,8 +1573,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,return addr; close_and_free_vma: -if (file && !vms.closed_vm_ops && vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close (vma); +vma_close (vma);if (file || vma->vm_file) { unmap_and_free_vma:
@@ -1934,7 +1933,7 @@ void exit_mmap(struct mm_struct *mm) do { if (vma->vm_flags & VM_ACCOUNT) nr_accounted += vma_pages(vma); - remove_vma(vma, /* unreachable = */ true, /* closed = */ false); + remove_vma(vma, /* unreachable = */ true); count++; cond_resched(); vma = vma_next(&vmi);
mm/nommu.c +1 -2
@@ -589,8 +589,7 @@ static int delete_vma_from_mm(struct vm_area_struct *vma)*/ static void delete_vma (struct mm_struct *mm,struct vm_area_struct *vma) { -if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close (vma); +vma_close (vma);if (vma->vm_file)fput (vma->vm_file);put_nommu_region (vma->vm_region);
mm/vma.c +5 -9
@@ -323,11 +323,10 @@ static bool can_vma_merge_right(struct vma_merge_struct *vmg,/* * Close a vm structure and free it. */ -void remove_vma (struct vm_area_struct *vma,bool unreachable,bool closed) +void remove_vma (struct vm_area_struct *vma,bool unreachable) {might_sleep (); -if (!closed && vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close (vma); +vma_close (vma);if (vma->vm_file)fput (vma->vm_file);mpol_put (vma_policy (vma));
@@ -1115,9 +1114,7 @@ void vms_clean_up_area(struct vma_munmap_struct *vms,vms_clear_ptes (vms, mas_detach, true);mas_set (mas_detach,0 );mas_for_each (mas_detach, vma,ULONG_MAX ) -if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close (vma); - vms->closed_vm_ops = true; +vma_close (vma); }/*
@@ -1160,7 +1157,7 @@ void vms_complete_munmap_vmas(struct vma_munmap_struct *vms,/* Remove and clean up vmas */ mas_set (mas_detach,0 );mas_for_each (mas_detach, vma,ULONG_MAX ) -remove_vma (vma,/* = */ false, vms->closed_vm_ops); +remove_vma (vma,/* unreachable = */ false);vm_unacct_memory (vms->nr_accounted);validate_mm (mm);
@@ -1684,8 +1681,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,return new_vma; out_vma_link: -if (new_vma->vm_ops && new_vma->vm_ops->close) - new_vma->vm_ops->close (new_vma); +vma_close (new_vma);if (new_vma->vm_file)fput (new_vma->vm_file);
mm/vma.h +1 -3
@@ -42,7 +42,6 @@ struct vma_munmap_struct {int vma_count;/* Number of vmas that will be removed */ bool unlock;/* Unlock after the munmap */ bool clear_ptes;/* If there are outstanding PTE to be cleared */ -bool closed_vm_ops;/* call_mmap() was encountered, so vmas may be closed */ /* 1 byte hole */ unsigned long nr_pages;/* Number of pages being removed */ unsigned long locked_vm;/* Number of locked pages */
@@ -198,7 +197,6 @@ static inline void init_vma_munmap(struct vma_munmap_struct *vms, vms->unmap_start =FIRST_USER_ADDRESS ; vms->unmap_end =USER_PGTABLES_CEILING ; vms->clear_ptes = false; - vms->closed_vm_ops = false; }#endif
@@ -269,7 +267,7 @@ int do_vmi_munmap(struct vma_iterator *vmi, struct mm_struct *mm, unsigned long start, size_t len, struct list_head *uf, bool unlock); -void remove_vma (struct vm_area_struct *vma,bool unreachable,bool closed); +void remove_vma (struct vm_area_struct *vma,bool unreachable);void unmap_region (struct ma_state *mas,struct vm_area_struct *vma,struct vm_area_struct *prev,struct vm_area_struct *next);
PATCH 1/1 mm: unconditionally close VMAs on error
Incorrect invocation of VMA callbacks when the VMA is no longer in a consistent state is bug prone and risky to perform.
With regards to the important vm_ops->close() callback We have gone to great lengths to try to track whether or not we ought to close VMAs.
Rather than doing so and risking making a mistake somewhere, instead unconditionally close and reset vma->vm_ops to an empty dummy operations set with a NULL .close operator.
We introduce a new function to do so - vma_close() - and simplify existing vms logic which tracked whether we needed to close or not.
This simplifies the logic, avoids incorrect double-calling of the .close() callback and allows us to update error paths to simply call vma_close() unconditionally - making VMA closure idempotent.
--- mm/internal.h | 7 +++++++ mm/mmap.c | 9 +++------ mm/nommu.c | 3 +-- mm/util.c | 15 +++++++++++++++ 4 files changed, 26 insertions(+), 8 deletions(-)
mm/internal.h +7 -0
@@ -46,6 +46,13 @@ void page_writeback_init(void);*/ int mmap_file (struct file *file,struct vm_area_struct *vma); +/* +* If the VMA has a close hook then close it, and since closing it might leave +* it in an inconsistent state which makes the use of any hooks suspect, clear +* them down by installing dummy empty hooks. +*/ +void vma_close (struct vm_area_struct *vma); +vm_fault_t do_swap_page (struct vm_fault *vmf); void free_pgtables(struct mmu_gather *tlb, struct vm_area_struct *start_vma,
mm/mmap.c +3 -6
@@ -176,8 +176,7 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma)struct vm_area_struct *next = vma->vm_next;might_sleep (); -if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close (vma); +vma_close (vma);if (vma->vm_file)fput (vma->vm_file);mpol_put (vma_policy (vma));
@@ -1901,8 +1900,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr,return addr; close_and_free_vma: -if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close (vma); +vma_close (vma); unmap_and_free_vma: vma->vm_file =NULL ;fput (file);
@@ -2788,8 +2786,7 @@ int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,return 0 ;/* Clean everything up if vma_adjust failed. */ -if (new->vm_ops && new->vm_ops->close) - new->vm_ops->close (new); +vma_close (new);if (new->vm_file)fput (new->vm_file);unlink_anon_vmas (new);
mm/nommu.c +1 -2
@@ -662,8 +662,7 @@ static void delete_vma_from_mm(struct vm_area_struct *vma)*/ static void delete_vma (struct mm_struct *mm,struct vm_area_struct *vma) { -if (vma->vm_ops && vma->vm_ops->close) - vma->vm_ops->close (vma); +vma_close (vma);if (vma->vm_file)fput (vma->vm_file);put_nommu_region (vma->vm_region);
mm/util.c +15 -0
@@ -1091,3 +1091,18 @@ int mmap_file(struct file *file, struct vm_area_struct *vma)return err; } + +void vma_close (struct vm_area_struct *vma) +{ +static const struct vm_operations_struct dummy_vm_ops = {}; + +if (vma->vm_ops && vma->vm_ops->close) { + vma->vm_ops->close (vma); + +/* +* The mapping is in an inconsistent state, and no further hooks +* may be invoked upon it. +*/ + vma->vm_ops = &dummy_vm_ops; + } +}
Signature · 1 line
2.47.0