* [PATCH v3 0/2] ARM: mm: fix use-after-free in show_pte()
@ 2026-06-26 7:30 Qi Xi
2026-06-26 7:30 ` [PATCH v3 1/2] ARM: mm: fix use-after-free in __do_user_fault() under CONFIG_DEBUG_USER Qi Xi
2026-06-26 7:30 ` [PATCH v3 2/2] ARM: mm: protect show_pte() in do_DataAbort() fallback path Qi Xi
0 siblings, 2 replies; 7+ messages in thread
From: Qi Xi @ 2026-06-26 7:30 UTC (permalink / raw)
To: Russell King, Andrew Morton
Cc: linux-arm-kernel, linux-kernel, Yuanbin Xie, Nanyong Sun, Qi Xi
This series fixes a use-after-free in show_pte() on 32-bit ARM.
show_pte() is called from __do_user_fault() after do_page_fault() has
already released mmap_read_lock. If another thread concurrently calls
munmap(), the page table pages can be freed while show_pte() is still
walking them, causing a use-after-free.
Patch 1 fixes the main path (__do_user_fault) with mmap_read_lock().
Patch 2 protects the do_DataAbort() fallback path with
mmap_read_trylock(), which enters show_pte() only for rare FSR types
(fsr_info entries with fn=do_bad).
v3: Split into two patches.
v2: Also fix do_DataAbort() fallback path.
Qi Xi (2):
ARM: mm: fix use-after-free in __do_user_fault() under
CONFIG_DEBUG_USER
ARM: mm: protect show_pte() in do_DataAbort() fallback path
arch/arm/mm/fault.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
--
2.33.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v3 1/2] ARM: mm: fix use-after-free in __do_user_fault() under CONFIG_DEBUG_USER 2026-06-26 7:30 [PATCH v3 0/2] ARM: mm: fix use-after-free in show_pte() Qi Xi @ 2026-06-26 7:30 ` Qi Xi 2026-06-26 9:44 ` Russell King 2026-06-26 7:30 ` [PATCH v3 2/2] ARM: mm: protect show_pte() in do_DataAbort() fallback path Qi Xi 1 sibling, 1 reply; 7+ messages in thread From: Qi Xi @ 2026-06-26 7:30 UTC (permalink / raw) To: Russell King, Andrew Morton Cc: linux-arm-kernel, linux-kernel, Yuanbin Xie, Nanyong Sun, Qi Xi When CONFIG_DEBUG_USER is enabled with user_debug=31 on 32-bit ARM, a user page fault triggers show_pte() via __do_user_fault() after do_page_fault() has already released mmap_read_lock. If another thread concurrently calls munmap(), the page table pages can be freed while show_pte() is still reading them, causing a use-after-free in show_pte(). The race can be reproduced on multi_v7_defconfig with: CONFIG_DEBUG_USER=y CONFIG_ARM_LPAE=y kernel command line: user_debug=31 A delay inserted in show_pte() for testing widens the race window and makes the UAF reliably reproducible. On LPAE, the race works as follows: CPU 0 (fault path) CPU 1 (munmap) munmap(page 0) -> clears PTE[0] PTE/PMD pages remain read page 0 -> page fault -> do_DataAbort() -> do_page_fault() -> lock_mm_and_find_vma() -> no VMA (mmap_read_lock released) -> __do_user_fault() -> show_pte(tsk->mm, addr) -> *pgd (valid) -> p4d/pud checks pass -> [delay] munmap(page 1) -> clears PTE[1] -> PTE/PMD pages freed -> PGD cleared -> pmd_offset(pud, addr) -> *pud=0 -> __va(0) -> dereference -> secondary data abort (kernel) Fix by taking mmap_read_lock() around show_pte() in __do_user_fault(). __do_user_fault() is called from process context with interrupts enabled, so the context can sleep and mmap_read_lock() is safe here. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Tested-by: Yuanbin Xie <xieyuanbin1@huawei.com> Suggested-by: Yuanbin Xie <xieyuanbin1@huawei.com> Signed-off-by: Qi Xi <xiqi2@huawei.com> --- arch/arm/mm/fault.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index e62cc4be5a..1f2a85e1fa 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -181,7 +181,9 @@ __do_user_fault(unsigned long addr, unsigned int fsr, unsigned int sig, pr_err("8<--- cut here ---\n"); pr_err("%s: unhandled page fault (%d) at 0x%08lx, code 0x%03x\n", tsk->comm, sig, addr, fsr); + mmap_read_lock(tsk->mm); show_pte(KERN_ERR, tsk->mm, addr); + mmap_read_unlock(tsk->mm); show_regs(regs); } #endif -- 2.33.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v3 1/2] ARM: mm: fix use-after-free in __do_user_fault() under CONFIG_DEBUG_USER 2026-06-26 7:30 ` [PATCH v3 1/2] ARM: mm: fix use-after-free in __do_user_fault() under CONFIG_DEBUG_USER Qi Xi @ 2026-06-26 9:44 ` Russell King 0 siblings, 0 replies; 7+ messages in thread From: Russell King @ 2026-06-26 9:44 UTC (permalink / raw) To: Qi Xi Cc: Andrew Morton, linux-arm-kernel, linux-kernel, Yuanbin Xie, Nanyong Sun On Fri, Jun 26, 2026 at 03:30:47PM +0800, Qi Xi wrote: > When CONFIG_DEBUG_USER is enabled with user_debug=31 on 32-bit ARM, > a user page fault triggers show_pte() via __do_user_fault() after > do_page_fault() has already released mmap_read_lock. If another > thread concurrently calls munmap(), the page table pages can be > freed while show_pte() is still reading them, causing a > use-after-free in show_pte(). > > The race can be reproduced on multi_v7_defconfig with: > CONFIG_DEBUG_USER=y > CONFIG_ARM_LPAE=y > kernel command line: user_debug=31 > > A delay inserted in show_pte() for testing widens the race window and > makes the UAF reliably reproducible. On LPAE, the race works as > follows: > > CPU 0 (fault path) CPU 1 (munmap) > munmap(page 0) -> clears PTE[0] > PTE/PMD pages remain > > read page 0 -> page fault > -> do_DataAbort() > -> do_page_fault() > -> lock_mm_and_find_vma() -> no VMA > (mmap_read_lock released) > -> __do_user_fault() > -> show_pte(tsk->mm, addr) > -> *pgd (valid) > -> p4d/pud checks pass > > -> [delay] munmap(page 1) > -> clears PTE[1] > -> PTE/PMD pages freed > -> PGD cleared > > -> pmd_offset(pud, addr) > -> *pud=0 -> __va(0) > -> dereference > -> secondary data abort (kernel) > > Fix by taking mmap_read_lock() around show_pte() in __do_user_fault(). > __do_user_fault() is called from process context with interrupts > enabled, so the context can sleep and mmap_read_lock() is safe here. This is a fault path which should only be called when something is already wrong, the mm lock may already be held (e.g. a kernel fault while already holding the mmap lock.) We can't take any locks here. -- RMK's Patch system: https://www.armlinux.org.uk/developer/patches/ FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last! ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v3 2/2] ARM: mm: protect show_pte() in do_DataAbort() fallback path 2026-06-26 7:30 [PATCH v3 0/2] ARM: mm: fix use-after-free in show_pte() Qi Xi 2026-06-26 7:30 ` [PATCH v3 1/2] ARM: mm: fix use-after-free in __do_user_fault() under CONFIG_DEBUG_USER Qi Xi @ 2026-06-26 7:30 ` Qi Xi 2026-06-26 9:45 ` Russell King 2026-06-26 10:16 ` Xie Yuanbin 1 sibling, 2 replies; 7+ messages in thread From: Qi Xi @ 2026-06-26 7:30 UTC (permalink / raw) To: Russell King, Andrew Morton Cc: linux-arm-kernel, linux-kernel, Yuanbin Xie, Nanyong Sun, Qi Xi The do_DataAbort() fallback path handles FSR types not serviced by do_page_fault() (fsr_info entries with fn=do_bad). This path also calls show_pte() without holding mmap_read_lock, exposing it to the same use-after-free issue. Since do_DataAbort() is an exception entry point that can be reached from contexts where sleeping is not allowed, use mmap_read_trylock(). If the lock cannot be acquired, the page table dump is skipped. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Suggested-by: Yuanbin Xie <xieyuanbin1@huawei.com> Signed-off-by: Qi Xi <xiqi2@huawei.com> --- arch/arm/mm/fault.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index 1f2a85e1fa..0a8fc40afe 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -638,7 +638,10 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) pr_alert("8<--- cut here ---\n"); pr_alert("Unhandled fault: %s (0x%03x) at 0x%08lx\n", inf->name, fsr, addr); - show_pte(KERN_ALERT, current->mm, addr); + if (mmap_read_trylock(current->mm)) { + show_pte(KERN_ALERT, current->mm, addr); + mmap_read_unlock(current->mm); + } arm_notify_die("", regs, inf->sig, inf->code, (void __user *)addr, fsr, 0); -- 2.33.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v3 2/2] ARM: mm: protect show_pte() in do_DataAbort() fallback path 2026-06-26 7:30 ` [PATCH v3 2/2] ARM: mm: protect show_pte() in do_DataAbort() fallback path Qi Xi @ 2026-06-26 9:45 ` Russell King 2026-06-26 10:16 ` Xie Yuanbin 1 sibling, 0 replies; 7+ messages in thread From: Russell King @ 2026-06-26 9:45 UTC (permalink / raw) To: Qi Xi Cc: Andrew Morton, linux-arm-kernel, linux-kernel, Yuanbin Xie, Nanyong Sun On Fri, Jun 26, 2026 at 03:30:48PM +0800, Qi Xi wrote: > The do_DataAbort() fallback path handles FSR types not serviced by > do_page_fault() (fsr_info entries with fn=do_bad). This path also > calls show_pte() without holding mmap_read_lock, exposing it to > the same use-after-free issue. > > Since do_DataAbort() is an exception entry point that can be reached > from contexts where sleeping is not allowed, use mmap_read_trylock(). > If the lock cannot be acquired, the page table dump is skipped. > > Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") > Suggested-by: Yuanbin Xie <xieyuanbin1@huawei.com> > Signed-off-by: Qi Xi <xiqi2@huawei.com> Same reason as patch 1. We can't take locks. -- RMK's Patch system: https://www.armlinux.org.uk/developer/patches/ FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last! ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 2/2] ARM: mm: protect show_pte() in do_DataAbort() fallback path 2026-06-26 7:30 ` [PATCH v3 2/2] ARM: mm: protect show_pte() in do_DataAbort() fallback path Qi Xi 2026-06-26 9:45 ` Russell King @ 2026-06-26 10:16 ` Xie Yuanbin 2026-06-26 12:37 ` Russell King 1 sibling, 1 reply; 7+ messages in thread From: Xie Yuanbin @ 2026-06-26 10:16 UTC (permalink / raw) To: xiqi2, linux Cc: akpm, linux-arm-kernel, linux-kernel, sunnanyong, xieyuanbin1 On Fri, 26 Jun 2026 15:30:48 +0800, Qi Xi wrote: > @@ -638,7 +638,10 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) > pr_alert("8<--- cut here ---\n"); > pr_alert("Unhandled fault: %s (0x%03x) at 0x%08lx\n", > inf->name, fsr, addr); > - show_pte(KERN_ALERT, current->mm, addr); > + if (mmap_read_trylock(current->mm)) { > + show_pte(KERN_ALERT, current->mm, addr); > + mmap_read_unlock(current->mm); > + } For kernel faults, `current->mm` maybe NULL, and `mmap_read_trylock(current->mm)` may cause a panic. Also, interrupts may be disabled here. I suggest that waiting for this patch to be merged first: https://lore.kernel.org/20260625122612.43501-1-xieyuanbin1@huawei.com which make sure that interrupts are enabled here. Then, something like: ```c if (user_mode(regs)) mmap_read_lock(current->mm); show_pte(KERN_ALERT, current->mm, addr); if (user_mode(regs)) mmap_read_unlock(current->mm); ``` ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v3 2/2] ARM: mm: protect show_pte() in do_DataAbort() fallback path 2026-06-26 10:16 ` Xie Yuanbin @ 2026-06-26 12:37 ` Russell King 0 siblings, 0 replies; 7+ messages in thread From: Russell King @ 2026-06-26 12:37 UTC (permalink / raw) To: Xie Yuanbin; +Cc: xiqi2, akpm, linux-arm-kernel, linux-kernel, sunnanyong On Fri, Jun 26, 2026 at 06:16:15PM +0800, Xie Yuanbin wrote: > On Fri, 26 Jun 2026 15:30:48 +0800, Qi Xi wrote: > > @@ -638,7 +638,10 @@ do_DataAbort(unsigned long addr, unsigned int fsr, struct pt_regs *regs) > > pr_alert("8<--- cut here ---\n"); > > pr_alert("Unhandled fault: %s (0x%03x) at 0x%08lx\n", > > inf->name, fsr, addr); > > - show_pte(KERN_ALERT, current->mm, addr); > > + if (mmap_read_trylock(current->mm)) { > > + show_pte(KERN_ALERT, current->mm, addr); > > + mmap_read_unlock(current->mm); > > + } > > For kernel faults, `current->mm` maybe NULL, and > `mmap_read_trylock(current->mm)` may cause a panic. > Also, interrupts may be disabled here. > > I suggest that waiting for this patch to be merged first: > https://lore.kernel.org/20260625122612.43501-1-xieyuanbin1@huawei.com > which make sure that interrupts are enabled here. No, it doesn't ensure that. show_pte() is also called from die_kernel_fault() and __do_kernel_fault(), which can be from a path that has interrupts disabled. See the comments in do_kernel_address_page_fault(). We are not "fixing" show_pte(), which is a diagnostic function when things go wrong in the kernel, and is there to *try* to give us information to diagnose what happened. It is *not* a function that is used routinely in the kernel. The fact it is racy with other CPUs may be a problem, but it isn't a big problem because when it's called, the system is practically dead anyway. -- RMK's Patch system: https://www.armlinux.org.uk/developer/patches/ FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last! ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-06-26 12:37 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-06-26 7:30 [PATCH v3 0/2] ARM: mm: fix use-after-free in show_pte() Qi Xi 2026-06-26 7:30 ` [PATCH v3 1/2] ARM: mm: fix use-after-free in __do_user_fault() under CONFIG_DEBUG_USER Qi Xi 2026-06-26 9:44 ` Russell King 2026-06-26 7:30 ` [PATCH v3 2/2] ARM: mm: protect show_pte() in do_DataAbort() fallback path Qi Xi 2026-06-26 9:45 ` Russell King 2026-06-26 10:16 ` Xie Yuanbin 2026-06-26 12:37 ` Russell King
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox