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 
syscall_enter_audit(struct pt_regs * regs,long syscall)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 
syscall_trace_enter(struct pt_regs * regs,long syscall,unsigned long work)20 long syscall_trace_enter(struct pt_regs *regs, long syscall,
21 				unsigned long work)
22 {
23 	long ret = 0;
24 
25 	/*
26 	 * Handle Syscall User Dispatch.  This must comes first, since
27 	 * the ABI here can be something that doesn't make sense for
28 	 * other syscall_work features.
29 	 */
30 	if (work & SYSCALL_WORK_SYSCALL_USER_DISPATCH) {
31 		if (syscall_user_dispatch(regs))
32 			return -1L;
33 	}
34 
35 	/* Handle ptrace */
36 	if (work & (SYSCALL_WORK_SYSCALL_TRACE | SYSCALL_WORK_SYSCALL_EMU)) {
37 		ret = ptrace_report_syscall_entry(regs);
38 		if (ret || (work & SYSCALL_WORK_SYSCALL_EMU))
39 			return -1L;
40 	}
41 
42 	/* Do seccomp after ptrace, to catch any tracer changes. */
43 	if (work & SYSCALL_WORK_SECCOMP) {
44 		ret = __secure_computing();
45 		if (ret == -1L)
46 			return ret;
47 	}
48 
49 	/* Either of the above might have changed the syscall number */
50 	syscall = syscall_get_nr(current, regs);
51 
52 	if (unlikely(work & SYSCALL_WORK_SYSCALL_TRACEPOINT)) {
53 		trace_sys_enter(regs, syscall);
54 		/*
55 		 * Probes or BPF hooks in the tracepoint may have changed the
56 		 * system call number as well.
57 		 */
58 		syscall = syscall_get_nr(current, regs);
59 	}
60 
61 	syscall_enter_audit(regs, syscall);
62 
63 	return ret ? : syscall;
64 }
65 
syscall_enter_from_user_mode_prepare(struct pt_regs * regs)66 noinstr void syscall_enter_from_user_mode_prepare(struct pt_regs *regs)
67 {
68 	enter_from_user_mode(regs);
69 	instrumentation_begin();
70 	local_irq_enable();
71 	instrumentation_end();
72 }
73 
74 /*
75  * If SYSCALL_EMU is set, then the only reason to report is when
76  * SINGLESTEP is set (i.e. PTRACE_SYSEMU_SINGLESTEP).  This syscall
77  * instruction has been already reported in syscall_enter_from_user_mode().
78  */
report_single_step(unsigned long work)79 static inline bool report_single_step(unsigned long work)
80 {
81 	if (work & SYSCALL_WORK_SYSCALL_EMU)
82 		return false;
83 
84 	return work & SYSCALL_WORK_SYSCALL_EXIT_TRAP;
85 }
86 
syscall_exit_work(struct pt_regs * regs,unsigned long work)87 void syscall_exit_work(struct pt_regs *regs, unsigned long work)
88 {
89 	bool step;
90 
91 	/*
92 	 * If the syscall was rolled back due to syscall user dispatching,
93 	 * then the tracers below are not invoked for the same reason as
94 	 * the entry side was not invoked in syscall_trace_enter(): The ABI
95 	 * of these syscalls is unknown.
96 	 */
97 	if (work & SYSCALL_WORK_SYSCALL_USER_DISPATCH) {
98 		if (unlikely(current->syscall_dispatch.on_dispatch)) {
99 			current->syscall_dispatch.on_dispatch = false;
100 			return;
101 		}
102 	}
103 
104 	audit_syscall_exit(regs);
105 
106 	if (work & SYSCALL_WORK_SYSCALL_TRACEPOINT)
107 		trace_sys_exit(regs, syscall_get_return_value(current, regs));
108 
109 	step = report_single_step(work);
110 	if (step || work & SYSCALL_WORK_SYSCALL_TRACE)
111 		ptrace_report_syscall_exit(regs, step);
112 }
113