In preparation for migrating arm64 to the generic entry infrastructure, decouple the decision to execute the syscall from the syscall number itself. The ptrace and seccomp now returns a boolean flag rather than overloading NO_SYSCALL (-1).
Changes: - arm64_syscall_trace_enter() returns bool: - false: abort syscall execution - true: proceed, with syscall number reread from regs - If we decide to execute the syscall, reread the syscall number and check if it is NO_SYSCALL again. Because when a tracer sets the syscall number to -1 and provides a custom return value in x0, this avoids calling invoke_syscall() which would overwrite the custom value with -ENOSYS by sys_ni_syscall(). Behavioral impact (unchanged): - User-issued syscall(-1): unchanged, returns -ENOSYS. - Tracer skip with custom x0: unchanged, returns custom x0 - Tracer skip without setting x0: unchanged, returns original argument (garbage) - Normal syscalls and seccomp rejects: unaffected No functional changes. Cc: Mark Rutland <[email protected]> Cc: Will Deacon <[email protected]> Cc: Catalin Marinas <[email protected]> Cc: Ada Couprie Diaz <[email protected]> Reviewed-by: Linus Walleij <[email protected]> Signed-off-by: Jinjie Ruan <[email protected]> --- arch/arm64/include/asm/syscall.h | 2 +- arch/arm64/kernel/ptrace.c | 8 ++++---- arch/arm64/kernel/syscall.c | 6 +++++- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h index 7cc872fde019..f19d4a8824ba 100644 --- a/arch/arm64/include/asm/syscall.h +++ b/arch/arm64/include/asm/syscall.h @@ -121,7 +121,7 @@ static inline int syscall_get_arch(struct task_struct *task) return AUDIT_ARCH_AARCH64; } -int arm64_syscall_trace_enter(struct pt_regs *regs, unsigned long flags); +bool arm64_syscall_trace_enter(struct pt_regs *regs, unsigned long flags); void arm64_syscall_exit_work(struct pt_regs *regs, unsigned long flags); static __always_inline void arm64_syscall_exit_to_user_mode_work(struct pt_regs *regs) diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c index 610a79c1072b..c304c062abbb 100644 --- a/arch/arm64/kernel/ptrace.c +++ b/arch/arm64/kernel/ptrace.c @@ -2470,14 +2470,14 @@ static inline void syscall_enter_audit(struct pt_regs *regs) static inline void syscall_enter_audit(struct pt_regs *regs) { } #endif -int arm64_syscall_trace_enter(struct pt_regs *regs, unsigned long flags) +bool arm64_syscall_trace_enter(struct pt_regs *regs, unsigned long flags) { int ret; if (flags & (_TIF_SYSCALL_EMU | _TIF_SYSCALL_TRACE)) { ret = report_syscall_entry(regs); if (ret || (flags & _TIF_SYSCALL_EMU)) - return NO_SYSCALL; + return false; /* ptrace might have changed thread flags */ flags = read_thread_flags(); @@ -2486,7 +2486,7 @@ int arm64_syscall_trace_enter(struct pt_regs *regs, unsigned long flags) /* Do the secure computing after ptrace; failures should be fast. */ if (unlikely(flags & _TIF_SECCOMP)) { if (!__seccomp_permit_syscall()) - return NO_SYSCALL; + return false; } if (test_thread_flag(TIF_SYSCALL_TRACEPOINT)) @@ -2495,7 +2495,7 @@ int arm64_syscall_trace_enter(struct pt_regs *regs, unsigned long flags) if (unlikely(audit_context())) syscall_enter_audit(regs); - return syscall_get_nr(current, regs); + return true; } static inline bool report_single_step(unsigned long flags) diff --git a/arch/arm64/kernel/syscall.c b/arch/arm64/kernel/syscall.c index 3488afd45d20..72c6e8b7ab21 100644 --- a/arch/arm64/kernel/syscall.c +++ b/arch/arm64/kernel/syscall.c @@ -108,7 +108,11 @@ static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr, */ if (scno == NO_SYSCALL) syscall_set_return_value(current, regs, -ENOSYS, 0); - scno = arm64_syscall_trace_enter(regs, read_thread_flags()); + if (!arm64_syscall_trace_enter(regs, read_thread_flags())) + goto trace_exit; + + /* Reread the syscall number as it might have been modified */ + scno = syscall_get_nr(current, regs); if (scno == NO_SYSCALL) goto trace_exit; } -- 2.34.1

