[PATCH] ARM: nwfpe: use task_pt_regs() to find user registers

From: Patrick Lister

Date: Wed Nov 11 2026 - 21:57:44 EST


NWFPE locates the trapping process's saved registers relative to
current_thread_info(), assuming thread_info sits at the bottom of the
kernel stack. Since ARM moved thread_info into task_struct, that
expression points past the end of the task_struct instead.

This breaks FPA instruction emulation, as the user registers are
never read or written and causes out-of-bounds reads and writes of
kernel memory beyond the task_struct. Any unprivileged process can
trigger this.

Fix by using task_pt_regs(current), which finds the registers on the
stack.

The bug was found with assistance from Claude Code (Anthropic), model
Claude Opus 5.5 (claude-opus-5-5).

Fixes: 18ed1c01a7dd ("ARM: smp: Enable THREAD_INFO_IN_TASK")
Fixes: 9c46929e7989 ("ARM: implement THREAD_INFO_IN_TASK for uniprocessor systems")
Cc: stable@xxxxxxxxxxxxxxx # 5.16+
Signed-off-by: Patrick Lister <patrick.lister.dev@xxxxxxxxx>
---
Build tested on Russell King's linux-arm tree with footbridge_defconfig
(CPU_SA110, FPE_NWFPE,THREAD_INFO_IN_TASK), W=1, no new warnings.

Runtime-tested on 6.12.116, where this patch applies unchanged
(arch/arm/nwfpe/fpa11.h is identical to mainline):

- QEMU versatilepb (ARM926) with OABI_COMPAT, FPE_NWFPE and KASAN:
a test that executes FLT and FIX gets the wrong result back and
KASAN reports "slab-out-of-bounds in PerformFLT" without this patch;
with it the result is correct and KASAN reports nothing.

- Nokia 770 (OMAP1710, ARM926, OABI userspace): fixes the maemo
battery daemon's FPA arithmetic which would incorrectly calculate
the remaining charge of the battery and power off the device.

Not runtime tested on mainline: since 25900bd99d4d ("ARM: limit OABI
support to StrongARM CPUs") NWFPE is StrongARM-only, and QEMU's only
StrongARM machine (collie) currently fails to boot mainline.

A reproducer is available on request.

arch/arm/nwfpe/fpa11.h | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/arch/arm/nwfpe/fpa11.h b/arch/arm/nwfpe/fpa11.h
index d51195d41f5b..25c327a73a27 100644
--- a/arch/arm/nwfpe/fpa11.h
+++ b/arch/arm/nwfpe/fpa11.h
@@ -13,13 +13,12 @@
#define GET_FPA11() ((FPA11 *)(&current_thread_info()->fpstate))

/*
- * The processes registers are always at the very top of the 8K
- * stack+task struct. Use the same method as 'current' uses to
- * reach them.
+ * The process's registers are saved at the top of its kernel stack.
*/
-#define GET_USERREG() ((struct pt_regs *)(THREAD_START_SP + (unsigned long)current_thread_info()) - 1)
+#define GET_USERREG() task_pt_regs(current)

#include <linux/thread_info.h>
+#include <linux/sched/task_stack.h>

/* includes */
#include "fpsr.h" /* FP control and status register definitions */
--
2.47.3