* [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
* [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 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
* 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