diff options
Diffstat (limited to 'arch/x86/entry/common.c')
-rw-r--r-- | arch/x86/entry/common.c | 84 |
1 files changed, 8 insertions, 76 deletions
diff --git a/arch/x86/entry/common.c b/arch/x86/entry/common.c index ec138e538c44..df56ca394877 100644 --- a/arch/x86/entry/common.c +++ b/arch/x86/entry/common.c | |||
@@ -64,20 +64,13 @@ static void do_audit_syscall_entry(struct pt_regs *regs, u32 arch) | |||
64 | } | 64 | } |
65 | 65 | ||
66 | /* | 66 | /* |
67 | * We can return 0 to resume the syscall or anything else to go to phase | 67 | * Returns the syscall nr to run (which should match regs->orig_ax) or -1 |
68 | * 2. If we resume the syscall, we need to put something appropriate in | 68 | * to skip the syscall. |
69 | * regs->orig_ax. | ||
70 | * | ||
71 | * NB: We don't have full pt_regs here, but regs->orig_ax and regs->ax | ||
72 | * are fully functional. | ||
73 | * | ||
74 | * For phase 2's benefit, our return value is: | ||
75 | * 0: resume the syscall | ||
76 | * 1: go to phase 2; no seccomp phase 2 needed | ||
77 | * anything else: go to phase 2; pass return value to seccomp | ||
78 | */ | 69 | */ |
79 | unsigned long syscall_trace_enter_phase1(struct pt_regs *regs, u32 arch) | 70 | static long syscall_trace_enter(struct pt_regs *regs) |
80 | { | 71 | { |
72 | u32 arch = in_ia32_syscall() ? AUDIT_ARCH_I386 : AUDIT_ARCH_X86_64; | ||
73 | |||
81 | struct thread_info *ti = pt_regs_to_thread_info(regs); | 74 | struct thread_info *ti = pt_regs_to_thread_info(regs); |
82 | unsigned long ret = 0; | 75 | unsigned long ret = 0; |
83 | u32 work; | 76 | u32 work; |
@@ -118,59 +111,9 @@ unsigned long syscall_trace_enter_phase1(struct pt_regs *regs, u32 arch) | |||
118 | sd.args[5] = regs->bp; | 111 | sd.args[5] = regs->bp; |
119 | } | 112 | } |
120 | 113 | ||
121 | BUILD_BUG_ON(SECCOMP_PHASE1_OK != 0); | 114 | ret = __secure_computing(&sd); |
122 | BUILD_BUG_ON(SECCOMP_PHASE1_SKIP != 1); | 115 | if (ret == -1) |
123 | 116 | return ret; | |
124 | ret = seccomp_phase1(&sd); | ||
125 | if (ret == SECCOMP_PHASE1_SKIP) { | ||
126 | regs->orig_ax = -1; | ||
127 | ret = 0; | ||
128 | } else if (ret != SECCOMP_PHASE1_OK) { | ||
129 | return ret; /* Go directly to phase 2 */ | ||
130 | } | ||
131 | |||
132 | work &= ~_TIF_SECCOMP; | ||
133 | } | ||
134 | #endif | ||
135 | |||
136 | /* Do our best to finish without phase 2. */ | ||
137 | if (work == 0) | ||
138 | return ret; /* seccomp and/or nohz only (ret == 0 here) */ | ||
139 | |||
140 | #ifdef CONFIG_AUDITSYSCALL | ||
141 | if (work == _TIF_SYSCALL_AUDIT) { | ||
142 | /* | ||
143 | * If there is no more work to be done except auditing, | ||
144 | * then audit in phase 1. Phase 2 always audits, so, if | ||
145 | * we audit here, then we can't go on to phase 2. | ||
146 | */ | ||
147 | do_audit_syscall_entry(regs, arch); | ||
148 | return 0; | ||
149 | } | ||
150 | #endif | ||
151 | |||
152 | return 1; /* Something is enabled that we can't handle in phase 1 */ | ||
153 | } | ||
154 | |||
155 | /* Returns the syscall nr to run (which should match regs->orig_ax). */ | ||
156 | long syscall_trace_enter_phase2(struct pt_regs *regs, u32 arch, | ||
157 | unsigned long phase1_result) | ||
158 | { | ||
159 | struct thread_info *ti = pt_regs_to_thread_info(regs); | ||
160 | long ret = 0; | ||
161 | u32 work = ACCESS_ONCE(ti->flags) & _TIF_WORK_SYSCALL_ENTRY; | ||
162 | |||
163 | if (IS_ENABLED(CONFIG_DEBUG_ENTRY)) | ||
164 | BUG_ON(regs != task_pt_regs(current)); | ||
165 | |||
166 | #ifdef CONFIG_SECCOMP | ||
167 | /* | ||
168 | * Call seccomp_phase2 before running the other hooks so that | ||
169 | * they can see any changes made by a seccomp tracer. | ||
170 | */ | ||
171 | if (phase1_result > 1 && seccomp_phase2(phase1_result)) { | ||
172 | /* seccomp failures shouldn't expose any additional code. */ | ||
173 | return -1; | ||
174 | } | 117 | } |
175 | #endif | 118 | #endif |
176 | 119 | ||
@@ -189,17 +132,6 @@ long syscall_trace_enter_phase2(struct pt_regs *regs, u32 arch, | |||
189 | return ret ?: regs->orig_ax; | 132 | return ret ?: regs->orig_ax; |
190 | } | 133 | } |
191 | 134 | ||
192 | long syscall_trace_enter(struct pt_regs *regs) | ||
193 | { | ||
194 | u32 arch = in_ia32_syscall() ? AUDIT_ARCH_I386 : AUDIT_ARCH_X86_64; | ||
195 | unsigned long phase1_result = syscall_trace_enter_phase1(regs, arch); | ||
196 | |||
197 | if (phase1_result == 0) | ||
198 | return regs->orig_ax; | ||
199 | else | ||
200 | return syscall_trace_enter_phase2(regs, arch, phase1_result); | ||
201 | } | ||
202 | |||
203 | #define EXIT_TO_USERMODE_LOOP_FLAGS \ | 135 | #define EXIT_TO_USERMODE_LOOP_FLAGS \ |
204 | (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_UPROBE | \ | 136 | (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_UPROBE | \ |
205 | _TIF_NEED_RESCHED | _TIF_USER_RETURN_NOTIFY) | 137 | _TIF_NEED_RESCHED | _TIF_USER_RETURN_NOTIFY) |