[RFC PATCH] arm64: mm: recover from kernel-mode SEA via exception table

Nguyen Ngoc Thang ngocthang2710.1999 at gmail.com
Sat Sep 12 06:55:46 PDT 2026


do_sea() unconditionally calls die() for a synchronous external
abort taken at EL1, without ever consulting the exception table.
This is inconsistent with __do_kernel_fault(), which does check
fixup_exception() before giving up.

A SEA at EL1 is reachable and recoverable: futex's LL/SC atomic ops
(__llsc_futex_cmpxchg/__llsc_futex_atomic_*) carry
_ASM_EXTABLE_UACCESS_ERR entries, but if the user address is mapped
to Device memory (e.g. a PCI BAR obtained via sysfs "resourceN" and
mmap'd MAP_FIXED), the LDXR/STLXR pair faults with a SEA instead of
a translation fault, and the extable fixup is never reached, so the
kernel oopses instead of returning -EFAULT to userspace.

Check fixup_exception() for kernel-mode SEAs before dying, mirroring
__do_kernel_fault(). No taint is added on the recovered path since
this is a userspace-triggerable condition, not a hardware failure;
a ratelimited warning keeps a trace of it.

Reported-by: syzbot+635d160c0d4133481520 at syzkaller.appspotmail.com
Link: https://syzkaller.appspot.com/bug?extid=635d160c0d4133481520
Signed-off-by: Nguyen Ngoc Thang <ngocthang2710.1999 at gmail.com>
---
Sent as RFC: verified by code inspection (ESR/DFSC decode matches
the extable-carrying LL/SC futex path) but not yet reproduced under
QEMU on my end; would appreciate a look before I chase the aarch64
repro further.

 arch/arm64/mm/fault.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 0b52557652be..fe5a5c5bbc27 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -878,6 +878,18 @@ static int do_sea(unsigned long far, unsigned long esr, struct pt_regs *regs)
 		return 0;
 	}
 
+	/*
+	 * A SEA at EL1 can happen from uaccess helpers (e.g. LDXR/STLXR
+	 * on a user page mapped as Device memory, as in the futex ops)
+	 * that carry an extable fixup. Let it return -EFAULT instead of
+	 * oopsing the kernel.
+	 */
+	if (!user_mode(regs) && fixup_exception(regs, esr)) {
+		pr_warn_ratelimited("Recovered SEA at kernel uaccess, addr=%#lx, esr=%#lx\n",
+				    far, esr);
+		return 0;
+	}
+
 	if (esr & ESR_ELx_FnV) {
 		siaddr = 0;
 	} else {
-- 
2.43.0




More information about the linux-arm-kernel mailing list