xref: /linux/kernel/entry/syscall-common.c (revision 03150a9f84b328f5c724b8ed9ff8600c2d7e2d7b)
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, unsigned long work)
21 {
22 	long syscall, 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 	/* Handle ptrace */
35 	if (work & (SYSCALL_WORK_SYSCALL_TRACE | SYSCALL_WORK_SYSCALL_EMU)) {
36 		ret = ptrace_report_syscall_entry(regs);
37 		if (ret || (work & SYSCALL_WORK_SYSCALL_EMU))
38 			return -1L;
39 	}
40 
41 	/* Do seccomp after ptrace, to catch any tracer changes. */
42 	if (work & SYSCALL_WORK_SECCOMP) {
43 		ret = __secure_computing();
44 		if (ret == -1L)
45 			return ret;
46 	}
47 
48 	/* Either of the above might have changed the syscall number */
49 	syscall = syscall_get_nr(current, regs);
50 
51 	if (unlikely(work & SYSCALL_WORK_SYSCALL_TRACEPOINT)) {
52 		trace_sys_enter(regs, syscall);
53 		/*
54 		 * Probes or BPF hooks in the tracepoint may have changed the
55 		 * system call number as well.
56 		 */
57 		syscall = syscall_get_nr(current, regs);
58 	}
59 
60 	syscall_enter_audit(regs, syscall);
61 
62 	return ret ? : syscall;
63 }
64 
65 /*
66  * If SYSCALL_EMU is set, then the only reason to report is when
67  * SINGLESTEP is set (i.e. PTRACE_SYSEMU_SINGLESTEP).  This syscall
68  * instruction has been already reported in syscall_enter_from_user_mode().
69  */
70 static inline bool report_single_step(unsigned long work)
71 {
72 	if (work & SYSCALL_WORK_SYSCALL_EMU)
73 		return false;
74 
75 	return work & SYSCALL_WORK_SYSCALL_EXIT_TRAP;
76 }
77 
78 void syscall_exit_work(struct pt_regs *regs, unsigned long work)
79 {
80 	bool step;
81 
82 	/*
83 	 * If the syscall was rolled back due to syscall user dispatching,
84 	 * then the tracers below are not invoked for the same reason as
85 	 * the entry side was not invoked in syscall_trace_enter(): The ABI
86 	 * of these syscalls is unknown.
87 	 */
88 	if (work & SYSCALL_WORK_SYSCALL_USER_DISPATCH) {
89 		if (unlikely(current->syscall_dispatch.on_dispatch)) {
90 			current->syscall_dispatch.on_dispatch = false;
91 			return;
92 		}
93 	}
94 
95 	audit_syscall_exit(regs);
96 
97 	if (work & SYSCALL_WORK_SYSCALL_TRACEPOINT)
98 		trace_sys_exit(regs, syscall_get_return_value(current, regs));
99 
100 	step = report_single_step(work);
101 	if (step || work & SYSCALL_WORK_SYSCALL_TRACE)
102 		ptrace_report_syscall_exit(regs, step);
103 }
104