According to the ptrace man page (Syscall-stops section): "If the tracee was restarted by PTRACE_SYSCALL or PTRACE_SYSEMU, the tracee enters syscall-enter-stop just prior to entering any system call (which will not be executed if the restart was using PTRACE_SYSEMU, regardless of any change made to registers at this point or how the tracee is restarted after this stop). ... If the tracee is continued using any other method (including PTRACE_SYSEMU), no syscall-exit-stop occurs. Note that all mentions PTRACE_SYSEMU apply equally to PTRACE_SYSEMU_SINGLESTEP." When a tracer uses PTRACE_SYSEMU_SINGLESTEP, both _TIF_SYSCALL_EMU and _TIF_SINGLESTEP are set. Currently, arm64 unconditionally reports a syscall exit stop whenever _TIF_SINGLESTEP is set, regardless of the emulation state. This deviates from the documented behavior and breaks consistency with x86, where PTRACE_SYSEMU_SINGLESTEP does not trigger a redundant exit stop. [Changes] - Introduce report_single_step(): Returns false when _TIF_SYSCALL_EMU is set, ensuring emulated syscalls do not trigger a duplicate report. - Update syscall_exit_work(): Use the new helper instead of directly checking _TIF_SINGLESTEP. [Impact] - PTRACE_SINGLESTEP: Continues to report exit stops for actual instructions. - PTRACE_SYSEMU: Continues to skip exit stops. - PTRACE_SYSEMU_SINGLESTEP: Now correctly skips the redundant exit stop, aligning arm64 with documentation and the generic entry infrastructure. Cc: Mark Rutland Cc: Will Deacon Cc: Catalin Marinas Fixes: ac2081cdc4d9 ("arm64: ptrace: Consistently use pseudo-singlestep exceptions") Reviewed-by: Ada Couprie Diaz Reviewed-by: Linus Walleij Reviewed-by: Yeoreum Yun Signed-off-by: Jinjie Ruan --- arch/arm64/kernel/ptrace.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 924805e0c8da..4e9c71d477a5 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -2482,16 +2482,26 @@ int syscall_trace_enter(struct pt_regs *regs) return regs->syscallno; } +static inline bool report_single_step(unsigned long flags) +{ + if (flags & _TIF_SYSCALL_EMU) + return false; + + return flags & _TIF_SINGLESTEP; +} + void syscall_trace_exit(struct pt_regs *regs) { unsigned long flags = read_thread_flags(); + bool step; audit_syscall_exit(regs); if (flags & _TIF_SYSCALL_TRACEPOINT) trace_sys_exit(regs, syscall_get_return_value(current, regs)); - if (flags & (_TIF_SYSCALL_TRACE | _TIF_SINGLESTEP)) + step = report_single_step(flags); + if (step || flags & _TIF_SYSCALL_TRACE) report_syscall_exit(regs); rseq_syscall(regs); -- 2.34.1