From 1a5d4f9174e10bd28d367040e4c770a1295af2e2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 12 Aug 2025 19:22:08 +0200 Subject: [PATCH] 6.15-stable patches added patches: mm-fix-a-uaf-when-vma-mm-is-freed-after-vma-vm_refcnt-got-dropped.patch --- ...reed-after-vma-vm_refcnt-got-dropped.patch | 139 ++++++++++++++++++ queue-6.15/series | 1 + 2 files changed, 140 insertions(+) create mode 100644 queue-6.15/mm-fix-a-uaf-when-vma-mm-is-freed-after-vma-vm_refcnt-got-dropped.patch diff --git a/queue-6.15/mm-fix-a-uaf-when-vma-mm-is-freed-after-vma-vm_refcnt-got-dropped.patch b/queue-6.15/mm-fix-a-uaf-when-vma-mm-is-freed-after-vma-vm_refcnt-got-dropped.patch new file mode 100644 index 0000000000..0129129dd3 --- /dev/null +++ b/queue-6.15/mm-fix-a-uaf-when-vma-mm-is-freed-after-vma-vm_refcnt-got-dropped.patch @@ -0,0 +1,139 @@ +From 9bbffee67ffd16360179327b57f3b1245579ef08 Mon Sep 17 00:00:00 2001 +From: Suren Baghdasaryan +Date: Mon, 28 Jul 2025 10:53:55 -0700 +Subject: mm: fix a UAF when vma->mm is freed after vma->vm_refcnt got dropped + +From: Suren Baghdasaryan + +commit 9bbffee67ffd16360179327b57f3b1245579ef08 upstream. + +By inducing delays in the right places, Jann Horn created a reproducer for +a hard to hit UAF issue that became possible after VMAs were allowed to be +recycled by adding SLAB_TYPESAFE_BY_RCU to their cache. + +Race description is borrowed from Jann's discovery report: +lock_vma_under_rcu() looks up a VMA locklessly with mas_walk() under +rcu_read_lock(). At that point, the VMA may be concurrently freed, and it +can be recycled by another process. vma_start_read() then increments the +vma->vm_refcnt (if it is in an acceptable range), and if this succeeds, +vma_start_read() can return a recycled VMA. + +In this scenario where the VMA has been recycled, lock_vma_under_rcu() +will then detect the mismatching ->vm_mm pointer and drop the VMA through +vma_end_read(), which calls vma_refcount_put(). vma_refcount_put() drops +the refcount and then calls rcuwait_wake_up() using a copy of vma->vm_mm. +This is wrong: It implicitly assumes that the caller is keeping the VMA's +mm alive, but in this scenario the caller has no relation to the VMA's mm, +so the rcuwait_wake_up() can cause UAF. + +The diagram depicting the race: +T1 T2 T3 +== == == +lock_vma_under_rcu + mas_walk + + mmap + + vma_start_read + __refcount_inc_not_zero_limited_acquire + munmap + __vma_enter_locked + refcount_add_not_zero + vma_end_read + vma_refcount_put + __refcount_dec_and_test + rcuwait_wait_event + + rcuwait_wake_up [UAF] + +Note that rcuwait_wait_event() in T3 does not block because refcount was +already dropped by T1. At this point T3 can exit and free the mm causing +UAF in T1. + +To avoid this we move vma->vm_mm verification into vma_start_read() and +grab vma->vm_mm to stabilize it before vma_refcount_put() operation. + +[surenb@google.com: v3] + Link: https://lkml.kernel.org/r/20250729145709.2731370-1-surenb@google.com +Link: https://lkml.kernel.org/r/20250728175355.2282375-1-surenb@google.com +Fixes: 3104138517fc ("mm: make vma cache SLAB_TYPESAFE_BY_RCU") +Signed-off-by: Suren Baghdasaryan +Reported-by: Jann Horn +Closes: https://lore.kernel.org/all/CAG48ez0-deFbVH=E3jbkWx=X3uVbd8nWeo6kbJPQ0KoUD+m2tA@mail.gmail.com/ +Reviewed-by: Vlastimil Babka +Acked-by: Lorenzo Stoakes +Cc: Jann Horn +Cc: Liam Howlett +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Suren Baghdasaryan +--- + include/linux/mm.h | 30 ++++++++++++++++++++++++++++++ + mm/memory.c | 3 +-- + 2 files changed, 31 insertions(+), 2 deletions(-) + +--- a/include/linux/mm.h ++++ b/include/linux/mm.h +@@ -33,6 +33,7 @@ + #include + #include + #include ++#include + + struct mempolicy; + struct anon_vma; +@@ -716,6 +717,10 @@ static inline void vma_refcount_put(stru + * reused and attached to a different mm before we lock it. + * Returns the vma on success, NULL on failure to lock and EAGAIN if vma got + * detached. ++ * ++ * WARNING! The vma passed to this function cannot be used if the function ++ * fails to lock it because in certain cases RCU lock is dropped and then ++ * reacquired. Once RCU lock is dropped the vma can be concurently freed. + */ + static inline struct vm_area_struct *vma_start_read(struct mm_struct *mm, + struct vm_area_struct *vma) +@@ -745,6 +750,31 @@ static inline struct vm_area_struct *vma + } + + rwsem_acquire_read(&vma->vmlock_dep_map, 0, 1, _RET_IP_); ++ ++ /* ++ * If vma got attached to another mm from under us, that mm is not ++ * stable and can be freed in the narrow window after vma->vm_refcnt ++ * is dropped and before rcuwait_wake_up(mm) is called. Grab it before ++ * releasing vma->vm_refcnt. ++ */ ++ if (unlikely(vma->vm_mm != mm)) { ++ /* Use a copy of vm_mm in case vma is freed after we drop vm_refcnt */ ++ struct mm_struct *other_mm = vma->vm_mm; ++ ++ /* ++ * __mmdrop() is a heavy operation and we don't need RCU ++ * protection here. Release RCU lock during these operations. ++ * We reinstate the RCU read lock as the caller expects it to ++ * be held when this function returns even on error. ++ */ ++ rcu_read_unlock(); ++ mmgrab(other_mm); ++ vma_refcount_put(vma); ++ mmdrop(other_mm); ++ rcu_read_lock(); ++ return NULL; ++ } ++ + /* + * Overflow of vm_lock_seq/mm_lock_seq might produce false locked result. + * False unlocked result is impossible because we modify and check +--- a/mm/memory.c ++++ b/mm/memory.c +@@ -6554,8 +6554,7 @@ retry: + */ + + /* Check if the vma we locked is the right one. */ +- if (unlikely(vma->vm_mm != mm || +- address < vma->vm_start || address >= vma->vm_end)) ++ if (unlikely(address < vma->vm_start || address >= vma->vm_end)) + goto inval_end_read; + + rcu_read_unlock(); diff --git a/queue-6.15/series b/queue-6.15/series index b4c8d65008..43adcaf5b0 100644 --- a/queue-6.15/series +++ b/queue-6.15/series @@ -477,3 +477,4 @@ usb-gadget-f_hid-fix-memory-leak-in-hidg_bind-error-path.patch hid-core-harden-s32ton-against-conversion-to-0-bits.patch hid-apple-avoid-setting-up-battery-timer-for-devices-without-battery.patch usb-gadget-fix-use-after-free-in-composite_dev_cleanup.patch +mm-fix-a-uaf-when-vma-mm-is-freed-after-vma-vm_refcnt-got-dropped.patch -- 2.47.3