From: Greg Kroah-Hartman Date: Thu, 1 Sep 2022 18:50:44 +0000 (+0200) Subject: 5.4-stable patches X-Git-Tag: v4.9.327~28 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2e890a7348d8b13bb2f0e8b1fd589773a3f7c824;p=thirdparty%2Fkernel%2Fstable-queue.git 5.4-stable patches added patches: mm-rmap-fix-anon_vma-degree-ambiguity-leading-to-double-reuse.patch --- diff --git a/queue-5.4/mm-rmap-fix-anon_vma-degree-ambiguity-leading-to-double-reuse.patch b/queue-5.4/mm-rmap-fix-anon_vma-degree-ambiguity-leading-to-double-reuse.patch new file mode 100644 index 00000000000..f2f5ad34ee2 --- /dev/null +++ b/queue-5.4/mm-rmap-fix-anon_vma-degree-ambiguity-leading-to-double-reuse.patch @@ -0,0 +1,168 @@ +From 2555283eb40df89945557273121e9393ef9b542b Mon Sep 17 00:00:00 2001 +From: Jann Horn +Date: Wed, 31 Aug 2022 19:06:00 +0200 +Subject: mm/rmap: Fix anon_vma->degree ambiguity leading to double-reuse + +From: Jann Horn + +commit 2555283eb40df89945557273121e9393ef9b542b upstream. + +anon_vma->degree tracks the combined number of child anon_vmas and VMAs +that use the anon_vma as their ->anon_vma. + +anon_vma_clone() then assumes that for any anon_vma attached to +src->anon_vma_chain other than src->anon_vma, it is impossible for it to +be a leaf node of the VMA tree, meaning that for such VMAs ->degree is +elevated by 1 because of a child anon_vma, meaning that if ->degree +equals 1 there are no VMAs that use the anon_vma as their ->anon_vma. + +This assumption is wrong because the ->degree optimization leads to leaf +nodes being abandoned on anon_vma_clone() - an existing anon_vma is +reused and no new parent-child relationship is created. So it is +possible to reuse an anon_vma for one VMA while it is still tied to +another VMA. + +This is an issue because is_mergeable_anon_vma() and its callers assume +that if two VMAs have the same ->anon_vma, the list of anon_vmas +attached to the VMAs is guaranteed to be the same. When this assumption +is violated, vma_merge() can merge pages into a VMA that is not attached +to the corresponding anon_vma, leading to dangling page->mapping +pointers that will be dereferenced during rmap walks. + +Fix it by separately tracking the number of child anon_vmas and the +number of VMAs using the anon_vma as their ->anon_vma. + +Fixes: 7a3ef208e662 ("mm: prevent endless growth of anon_vma hierarchy") +Cc: stable@kernel.org +Acked-by: Michal Hocko +Acked-by: Vlastimil Babka +Signed-off-by: Jann Horn +Signed-off-by: Linus Torvalds +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/rmap.h | 7 +++++-- + mm/rmap.c | 31 +++++++++++++++++-------------- + 2 files changed, 22 insertions(+), 16 deletions(-) + +--- a/include/linux/rmap.h ++++ b/include/linux/rmap.h +@@ -39,12 +39,15 @@ struct anon_vma { + atomic_t refcount; + + /* +- * Count of child anon_vmas and VMAs which points to this anon_vma. ++ * Count of child anon_vmas. Equals to the count of all anon_vmas that ++ * have ->parent pointing to this one, including itself. + * + * This counter is used for making decision about reusing anon_vma + * instead of forking new one. See comments in function anon_vma_clone. + */ +- unsigned degree; ++ unsigned long num_children; ++ /* Count of VMAs whose ->anon_vma pointer points to this object. */ ++ unsigned long num_active_vmas; + + struct anon_vma *parent; /* Parent of this anon_vma */ + +--- a/mm/rmap.c ++++ b/mm/rmap.c +@@ -83,7 +83,8 @@ static inline struct anon_vma *anon_vma_ + anon_vma = kmem_cache_alloc(anon_vma_cachep, GFP_KERNEL); + if (anon_vma) { + atomic_set(&anon_vma->refcount, 1); +- anon_vma->degree = 1; /* Reference for first vma */ ++ anon_vma->num_children = 0; ++ anon_vma->num_active_vmas = 0; + anon_vma->parent = anon_vma; + /* + * Initialise the anon_vma root to point to itself. If called +@@ -191,6 +192,7 @@ int __anon_vma_prepare(struct vm_area_st + anon_vma = anon_vma_alloc(); + if (unlikely(!anon_vma)) + goto out_enomem_free_avc; ++ anon_vma->num_children++; /* self-parent link for new root */ + allocated = anon_vma; + } + +@@ -200,8 +202,7 @@ int __anon_vma_prepare(struct vm_area_st + if (likely(!vma->anon_vma)) { + vma->anon_vma = anon_vma; + anon_vma_chain_link(vma, avc, anon_vma); +- /* vma reference or self-parent link for new root */ +- anon_vma->degree++; ++ anon_vma->num_active_vmas++; + allocated = NULL; + avc = NULL; + } +@@ -280,19 +281,19 @@ int anon_vma_clone(struct vm_area_struct + anon_vma_chain_link(dst, avc, anon_vma); + + /* +- * Reuse existing anon_vma if its degree lower than two, +- * that means it has no vma and only one anon_vma child. ++ * Reuse existing anon_vma if it has no vma and only one ++ * anon_vma child. + * +- * Do not chose parent anon_vma, otherwise first child +- * will always reuse it. Root anon_vma is never reused: ++ * Root anon_vma is never reused: + * it has self-parent reference and at least one child. + */ +- if (!dst->anon_vma && anon_vma != src->anon_vma && +- anon_vma->degree < 2) ++ if (!dst->anon_vma && ++ anon_vma->num_children < 2 && ++ anon_vma->num_active_vmas == 0) + dst->anon_vma = anon_vma; + } + if (dst->anon_vma) +- dst->anon_vma->degree++; ++ dst->anon_vma->num_active_vmas++; + unlock_anon_vma_root(root); + return 0; + +@@ -342,6 +343,7 @@ int anon_vma_fork(struct vm_area_struct + anon_vma = anon_vma_alloc(); + if (!anon_vma) + goto out_error; ++ anon_vma->num_active_vmas++; + avc = anon_vma_chain_alloc(GFP_KERNEL); + if (!avc) + goto out_error_free_anon_vma; +@@ -362,7 +364,7 @@ int anon_vma_fork(struct vm_area_struct + vma->anon_vma = anon_vma; + anon_vma_lock_write(anon_vma); + anon_vma_chain_link(vma, avc, anon_vma); +- anon_vma->parent->degree++; ++ anon_vma->parent->num_children++; + anon_vma_unlock_write(anon_vma); + + return 0; +@@ -394,7 +396,7 @@ void unlink_anon_vmas(struct vm_area_str + * to free them outside the lock. + */ + if (RB_EMPTY_ROOT(&anon_vma->rb_root.rb_root)) { +- anon_vma->parent->degree--; ++ anon_vma->parent->num_children--; + continue; + } + +@@ -402,7 +404,7 @@ void unlink_anon_vmas(struct vm_area_str + anon_vma_chain_free(avc); + } + if (vma->anon_vma) +- vma->anon_vma->degree--; ++ vma->anon_vma->num_active_vmas--; + unlock_anon_vma_root(root); + + /* +@@ -413,7 +415,8 @@ void unlink_anon_vmas(struct vm_area_str + list_for_each_entry_safe(avc, next, &vma->anon_vma_chain, same_vma) { + struct anon_vma *anon_vma = avc->anon_vma; + +- VM_WARN_ON(anon_vma->degree); ++ VM_WARN_ON(anon_vma->num_children); ++ VM_WARN_ON(anon_vma->num_active_vmas); + put_anon_vma(anon_vma); + + list_del(&avc->same_vma); diff --git a/queue-5.4/series b/queue-5.4/series index 1bd4b42f2f8..e571311c131 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -58,3 +58,4 @@ hid-hidraw-fix-memory-leak-in-hidraw_release.patch fbdev-fb_pm2fb-avoid-potential-divide-by-zero-error.patch ftrace-fix-null-pointer-dereference-in-is_ftrace_trampoline-when-ftrace-is-dead.patch bpf-don-t-redirect-packets-with-invalid-pkt_len.patch +mm-rmap-fix-anon_vma-degree-ambiguity-leading-to-double-reuse.patch