From: Greg Kroah-Hartman Date: Thu, 1 Sep 2022 18:50:28 +0000 (+0200) Subject: 4.14-stable patches X-Git-Tag: v4.9.327~30 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=767baf8cf70e7c2726fc652c9a2a4bb74321f43c;p=thirdparty%2Fkernel%2Fstable-queue.git 4.14-stable patches added patches: mm-rmap-fix-anon_vma-degree-ambiguity-leading-to-double-reuse.patch --- diff --git a/queue-4.14/mm-rmap-fix-anon_vma-degree-ambiguity-leading-to-double-reuse.patch b/queue-4.14/mm-rmap-fix-anon_vma-degree-ambiguity-leading-to-double-reuse.patch new file mode 100644 index 00000000000..1efc49bacd6 --- /dev/null +++ b/queue-4.14/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 +@@ -82,7 +82,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 +@@ -190,6 +191,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; + } + +@@ -199,8 +201,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; + } +@@ -279,19 +280,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; + +@@ -341,6 +342,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; +@@ -361,7 +363,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; +@@ -393,7 +395,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; + } + +@@ -401,7 +403,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); + + /* +@@ -412,7 +414,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-4.14/series b/queue-4.14/series index c543a6450d1..5d852fb13a6 100644 --- a/queue-4.14/series +++ b/queue-4.14/series @@ -34,3 +34,4 @@ media-pvrusb2-fix-memory-leak-in-pvr_probe.patch 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 +mm-rmap-fix-anon_vma-degree-ambiguity-leading-to-double-reuse.patch