[PATCH 04/14] alpha: fix syscall restart eligibility
From: Magnus Lindholm
Date: Tue Nov 03 2026 - 16:47:36 EST
After GENERIC_ENTRY, stale restart state or a successful syscall result
such as 512 can be mistaken for a restart code, corrupting r0 or the PC.
Clear restart eligibility after use and before returning to userspace,
including signal-context restore. Require a3 to indicate an error before
handling restart codes, preserving the saved arguments for replay.
Save the fourth argument before entry work can replace r19 with the
a3 error flag. Preserve that saved value when seccomp or ptrace skips
dispatch and supplies a restart error; dispatched syscalls still save
the argument after any ptrace changes.
Fixes: 83bd31c2840b ("alpha: enable GENERIC_ENTRY")
Signed-off-by: Magnus Lindholm <linmag7@xxxxxxxxx>
---
arch/alpha/include/asm/entry-common.h | 10 ++++++++++
arch/alpha/kernel/signal.c | 13 ++++++++++---
2 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/arch/alpha/include/asm/entry-common.h b/arch/alpha/include/asm/entry-common.h
index a811c73454d28dbc166b83b6f5cd550da339b5fb..85dabd1021da012269eb28acb3ce2da21e0ad706 100644
--- a/arch/alpha/include/asm/entry-common.h
+++ b/arch/alpha/include/asm/entry-common.h
@@ -4,6 +4,7 @@
#include <asm/stacktrace.h> /* For on_thread_stack() */
#include <asm/syscall.h>
+#include <asm/thread_info.h>
#define arch_exit_to_user_mode_work arch_exit_to_user_mode_work
@@ -11,4 +12,13 @@ static __always_inline void arch_exit_to_user_mode_work(struct pt_regs *regs,
unsigned long ti_work)
{
}
+
+#define arch_exit_to_user_mode_prepare arch_exit_to_user_mode_prepare
+static inline void arch_exit_to_user_mode_prepare(struct pt_regs *regs,
+ unsigned long ti_work)
+{
+ /* An interrupt after returning to user mode must not restart a syscall. */
+ current_thread_info()->syscall_saved_nr = 0;
+}
+
#endif
diff --git a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c
index 3fae37b4593f22f07a375ab7bc0b0712d0130a07..ab32affa09c41152eabd1f3be92a126a85fb7a2d 100644
--- a/arch/alpha/kernel/signal.c
+++ b/arch/alpha/kernel/signal.c
@@ -154,6 +154,7 @@ restore_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs)
struct switch_stack *sw = (struct switch_stack *)regs - 1;
current->restart_block.fn = do_no_restart_syscall;
+ current_thread_info()->syscall_saved_nr = 0;
current_thread_info()->status |= TS_SAVED_FP | TS_RESTORE_FP;
sw->r26 = (unsigned long) ret_from_sys_call;
@@ -497,17 +498,20 @@ do_signal(struct pt_regs *regs, unsigned long r0, unsigned long r19)
unsigned long single_stepping = ptrace_cancel_bpt(current);
struct ksignal ksig;
+ /* Consume restart state once, even if the exit loop repeats. */
+ current_thread_info()->syscall_saved_nr = 0;
+
/* This lets the debugger run, ... */
if (get_signal(&ksig)) {
/* ... so re-check the single stepping. */
single_stepping |= ptrace_cancel_bpt(current);
/* Whee! Actually deliver the signal. */
- if (r0)
+ if (r0 && regs->r19)
syscall_restart(r0, r19, regs, &ksig.ka);
handle_signal(&ksig, regs);
} else {
single_stepping |= ptrace_cancel_bpt(current);
- if (r0) {
+ if (r0 && regs->r19) {
switch (regs->r0) {
case ERESTARTNOHAND:
case ERESTARTSYS:
@@ -571,6 +575,7 @@ alpha_syscall_enter_select(struct pt_regs *regs, long syscall)
ti->syscall_meta = 0;
ti->syscall_saved_nr = syscall;
+ ti->syscall_saved_r19 = saved_r19;
if (!(ti->status & TS_SAVED_FP)) {
ti->status |= TS_SAVED_FP;
@@ -614,7 +619,9 @@ alpha_finish_syscall_to_user_mode(struct pt_regs *regs, long ret)
unsigned long meta = ti->syscall_meta;
ti->syscall_meta = 0;
- ti->syscall_saved_r19 = regs->r19;
+ /* Entry work may have replaced argument 4 with the a3 error flag. */
+ if (!(meta & ALPHA_SYSCALL_META_SKIP))
+ ti->syscall_saved_r19 = regs->r19;
instrumentation_begin();
--
2.43.0