Move the rseq_syscall() check earlier in the syscall exit path to ensure
it operates on the original instruction pointer (regs->pc) before any
potential modification by a tracer.

[Background]
When CONFIG_DEBUG_RSEQ is enabled, rseq_syscall() verifies that a system
call was not executed within an rseq critical section by examining
regs->pc. If a violation is detected, it triggers a SIGSEGV.

[Problem]
Currently, arm64 invokes rseq_syscall() after report_syscall_exit().
However, during report_syscall_exit(), a ptrace tracer can modify the
task's instruction pointer via PTRACE_SETREGSET (with NT_PRSTATUS). This
leads to an inconsistency where rseq may analyze a post-trace PC instead
of the actual PC at the time of syscall exit.

[Why this matters]
The rseq check is intended to validate the execution context of the
syscall itself. Analyzing a tracer-modified PC can lead to incorrect
detection or missed violations. Moving the check earlier ensures rseq
sees the authentic state of the task.

[Alignment]
This change aligns arm64 with:
- Generic entry, which calls rseq_syscall() first.
- arm32 implementation, which also performs the check before audit.

[Impact]
There is no functional change to signal delivery; SIGSEGV will still be
processed in arm64_exit_to_user_mode() at the end of the exit path.

Cc: Mark Rutland <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Will Deacon <[email protected]>
Cc: Catalin Marinas <[email protected]>
Reviewed-by: Ada Couprie Diaz <[email protected]>
Reviewed-by: Linus Walleij <[email protected]>
Reviewed-by: Yeoreum Yun <[email protected]>
Reviewed-by: Kevin Brodsky <[email protected]>
Signed-off-by: Jinjie Ruan <[email protected]>
---
 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 96c03922baa5..72df4b84ec9a 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -2513,6 +2513,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)
@@ -2521,8 +2523,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


Reply via email to