[PATCH v21 02/15] arm64: ptrace: Check rseq critical section before reporting syscall exit

From: Jinjie Ruan

Date: Wed Nov 11 2026 - 06:44:36 EST


With CONFIG_DEBUG_RSEQ=y, rseq_syscall() kills the task with SIGSEGV if
the system call was issued from inside an rseq critical section. It
decides this from regs->pc.

arm64 calls rseq_syscall() after report_syscall_exit(), so a tracer
stopped at the syscall-exit-stop can change regs->pc with
PTRACE_SETREGSET before the check runs. The check then looks at the PC
written by the tracer, not the PC the system call was issued from:

- a system call issued inside a critical section is not detected if
the tracer moves the PC out of it;

- a system call issued outside a critical section kills the task with
SIGSEGV if the tracer moves the PC into it.

Call rseq_syscall() first, before audit, tracepoints and ptrace, as
generic entry (rseq_debug_syscall_return()), x86 before it moved to
generic entry, and arm32 already do.

Only kernels with CONFIG_DEBUG_RSEQ=y are affected. The option depends
on EXPERT and DEBUG_KERNEL, is off by default, and is otherwise a no-op
stub. Measured in QEMU with a traced task that issues getppid():

case before after
inside the CS, PC moved out not killed SIGSEGV
outside the CS, PC moved into the CS SIGSEGV exits normally

Fixes: 409d5db49867 ("arm64: rseq: Implement backend rseq calls and select HAVE_RSEQ")
Cc: Mark Rutland <mark.rutland@xxxxxxx>
Cc: Thomas Gleixner <tglx@xxxxxxxxxx>
Cc: Will Deacon <will@xxxxxxxxxx>
Cc: Catalin Marinas <catalin.marinas@xxxxxxx>
Reviewed-by: Ada Couprie Diaz <ada.coupriediaz@xxxxxxx>
Reviewed-by: Linus Walleij <linusw@xxxxxxxxxx>
Reviewed-by: Yeoreum Yun <yeoreum.yun@xxxxxxx>
Reviewed-by: Kevin Brodsky <kevin.brodsky@xxxxxxx>
Signed-off-by: Jinjie Ruan <ruanjinjie@xxxxxxxxxx>
---
arch/arm64/kernel/ptrace.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index 4baf04f20e67..a34d4893f4bf 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -2495,6 +2495,8 @@ void syscall_trace_exit(struct pt_regs *regs)
unsigned long flags = read_thread_flags();
bool step;

+ rseq_syscall(regs);
+
audit_syscall_exit(regs);

if (flags & _TIF_SYSCALL_TRACEPOINT)
@@ -2503,8 +2505,6 @@ void syscall_trace_exit(struct pt_regs *regs)
step = report_single_step(flags);
if (step || flags & _TIF_SYSCALL_TRACE)
report_syscall_exit(regs);
-
- rseq_syscall(regs);
}

/*
--
2.34.1