Skip to contentSkip to conversation

ARM: nwfpe: use task_pt_regs() to find user registers

PATCH In review Patrick Lister 4 participants 5 messages Nov 12

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 ARM: nwfpe: use task_pt_regs() to find user registers

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")

---
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(-)
arch/arm/nwfpe/fpa11.h +3 -4
index d51195d41f5b..25c327a73a27 100644
@@ -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 */
Signature · 1 line
2.47.3
#1 lore raw
Quoted 37 lines · Patrick Lister

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")

---
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.

Quoted 6 lines

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.

So NWFPE has been broken for 5 years and nobody noticed until now?

Reviewed-by on v1 1/1
#2 lore raw
In reply to Patrick Lister
Quoted 9 lines · Patrick Lister

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.

Thanks a lot for chasing this down!

As I understand it, this means that nobody has seriously used or tested OABI since 5.16. I last tested it myself in 5.14 when working on 4e57a4ddf6b0 ("ARM: 9107/1: syscall: always store thread_info->abi_syscall"), but it was quite hard to properly test it at the time, even getting an old Debian rootfs to work with a modern kernel and qemu was nontrivial.

Quoted 11 lines

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")

The patch looks fine, and it's clearly harmless to merge as there are no users, so please add it to the patch tracker according to the instructions at https://www.arm.linux.org.uk/developer/patches/info.php

Hopefully this way we can have OABI/NWFPE in a working state for the LTS backports when we remove it in the near future, in case anyone ever wants to play with it later.

I've added the maintainers for the Arm ports in gcc, qemu and binutils to Cc here, I believe all of these still have some level of OABI support that they kept just because Linux still handles it, but that can then also be removed.

Arnd

#3 lore raw
Quoted 21 lines · Arnd Bergmann
Quoted 9 lines · Patrick Lister

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.

Thanks a lot for chasing this down!

As I understand it, this means that nobody has seriously used or tested OABI since 5.16. I last tested it myself in 5.14 when working on 4e57a4ddf6b0 ("ARM: 9107/1: syscall: always store thread_info->abi_syscall"), but it was quite hard to properly test it at the time, even getting an old Debian rootfs to work with a modern kernel and qemu was nontrivial.

Quoted 4 lines

I've added the maintainers for the Arm ports in gcc, qemu and binutils to Cc here, I believe all of these still have some level of OABI support that they kept just because Linux still handles it, but that can then also be removed.

QEMU has already deprecated its OABI and NWFPE emulation as of our 11.0 release in April this year. We'll probably remove it in 12.0 in April next year. (Our deprecation policy only requires two releases of notice before removal, so we could have removed it for 11.2 if we'd remembered, but as we're already in freeze it's a bit late to do so now.)

thanks
-- PMM