[PATCH 1/4] alpha: fix stale user_stack_pointer()

From: Matt Turner

Date: Sun Oct 11 2026 - 16:17:47 EST


PALcode keeps the user stack pointer in the PCB, not the trap frame, so
pt_regs->usp is only valid where entry code stores it: entSys,
do_entArith() and do_entIF(). Interrupts, page faults and unaligned
traps leave it stale, and ptrace and /proc/<pid>/syscall read it
through user_stack_pointer().

A user-mode pt_regs is at the top of its task's stack, so find the
thread_info from it and use rdusp() for current or pcb.usp otherwise,
as KSTK_ESP() and the regset code already do.

Fixes: 8a2857bc2b30 ("alpha: enable regset-based ptrace and core dumps")
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Matt Turner <mattst88@xxxxxxxxx>
---
arch/alpha/include/asm/ptrace.h | 7 +------
arch/alpha/kernel/ptrace.c | 12 ++++++++++++
2 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/arch/alpha/include/asm/ptrace.h b/arch/alpha/include/asm/ptrace.h
index 430e8dc27ff7..45d1229344b6 100644
--- a/arch/alpha/include/asm/ptrace.h
+++ b/arch/alpha/include/asm/ptrace.h
@@ -26,12 +26,7 @@ static inline unsigned long regs_return_value(struct pt_regs *regs)
return regs->r0;
}

-/* Helpers for working with the user stack pointer */
-static inline unsigned long user_stack_pointer(struct pt_regs *regs)
-{
- /* Valid for user-mode regs */
- return regs->usp;
-}
+unsigned long user_stack_pointer(struct pt_regs *regs);

static __always_inline bool regs_irqs_disabled(struct pt_regs *regs)
{
diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c
index e1d8c2f69688..dba7e9905cfa 100644
--- a/arch/alpha/kernel/ptrace.c
+++ b/arch/alpha/kernel/ptrace.c
@@ -440,6 +440,18 @@ ptrace_cancel_bpt(struct task_struct * child)
return (nsaved != 0);
}

+/*
+ * PALcode keeps the user stack pointer in the PCB, not the trap frame.
+ * A user-mode pt_regs sits at the top of its task's stack, so the
+ * thread_info is at the base.
+ */
+unsigned long user_stack_pointer(struct pt_regs *regs)
+{
+ struct thread_info *ti = (void *)((unsigned long)regs & -THREAD_SIZE);
+
+ return ti == current_thread_info() ? rdusp() : ti->pcb.usp;
+}
+
void user_enable_single_step(struct task_struct *child)
{
/* Mark single stepping. */

--
2.55.0