Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [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