1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <linux/audit.h> 4 #include <linux/entry-common.h> 5 #include "common.h" 6 7 #define CREATE_TRACE_POINTS 8 #include <trace/events/syscalls.h> 9 10 static inline void syscall_enter_audit(struct pt_regs *regs, long syscall) 11 { 12 if (unlikely(audit_context())) { 13 unsigned long args[6]; 14 15 syscall_get_arguments(current, regs, args); 16 audit_syscall_entry(syscall, args[0], args[1], args[2], args[3]); 17 } 18 } 19 20 long syscall_trace_enter(struct pt_regs *regs, long syscall, unsigned long work) 21 { 22 long ret = 0; 23 24 /* 25 * Handle Syscall User Dispatch. This must comes first, since 26 * the ABI here can be something that doesn't make sense for 27 * other syscall_work features. 28 */ 29 if (work & SYSCALL_WORK_SYSCALL_USER_DISPATCH) { 30 if (syscall_user_dispatch(regs)) 31 return -1L; 32 } 33 34 /* 35 * User space got a time slice extension granted and relinquishes 36 * the CPU. The work stops the slice timer to avoid an extra round 37 * through hrtimer_interrupt(). 38 */ 39 if (work & SYSCALL_WORK_SYSCALL_RSEQ_SLICE) 40 rseq_syscall_enter_work(syscall); 41 42 /* Handle ptrace */ 43 if (work & (SYSCALL_WORK_SYSCALL_TRACE | SYSCALL_WORK_SYSCALL_EMU)) { 44 ret = ptrace_report_syscall_entry(regs); 45 if (ret || (work & SYSCALL_WORK_SYSCALL_EMU)) 46 return -1L; 47 } 48 49 /* Do seccomp after ptrace, to catch any tracer changes. */ 50 if (work & SYSCALL_WORK_SECCOMP) { 51 ret = __secure_computing(); 52 if (ret == -1L) 53 return ret; 54 } 55 56 /* Either of the above might have changed the syscall number */ 57 syscall = syscall_get_nr(current, regs); 58 59 if (unlikely(work & SYSCALL_WORK_SYSCALL_TRACEPOINT)) { 60 trace_sys_enter(regs, syscall); 61 /* 62 * Probes or BPF hooks in the tracepoint may have changed the 63 * system call number as well. 64 */ 65 syscall = syscall_get_nr(current, regs); 66 } 67 68 syscall_enter_audit(regs, syscall); 69 70 return ret ? : syscall; 71 } 72 73 /* 74 * If SYSCALL_EMU is set, then the only reason to report is when 75 * SINGLESTEP is set (i.e. PTRACE_SYSEMU_SINGLESTEP). This syscall 76 * instruction has been already reported in syscall_enter_from_user_mode(). 77 */ 78 static inline bool report_single_step(unsigned long work) 79 { 80 if (work & SYSCALL_WORK_SYSCALL_EMU) 81 return false; 82 83 return work & SYSCALL_WORK_SYSCALL_EXIT_TRAP; 84 } 85 86 void syscall_exit_work(struct pt_regs *regs, unsigned long work) 87 { 88 bool step; 89 90 /* 91 * If the syscall was rolled back due to syscall user dispatching, 92 * then the tracers below are not invoked for the same reason as 93 * the entry side was not invoked in syscall_trace_enter(): The ABI 94 * of these syscalls is unknown. 95 */ 96 if (work & SYSCALL_WORK_SYSCALL_USER_DISPATCH) { 97 if (unlikely(current->syscall_dispatch.on_dispatch)) { 98 current->syscall_dispatch.on_dispatch = false; 99 return; 100 } 101 } 102 103 audit_syscall_exit(regs); 104 105 if (work & SYSCALL_WORK_SYSCALL_TRACEPOINT) 106 trace_sys_exit(regs, syscall_get_return_value(current, regs)); 107 108 step = report_single_step(work); 109 if (step || work & SYSCALL_WORK_SYSCALL_TRACE) 110 ptrace_report_syscall_exit(regs, step); 111 } 112