diff options
Diffstat (limited to 'arch/x86/kernel/vsyscall_64.c')
-rw-r--r-- | arch/x86/kernel/vsyscall_64.c | 39 |
1 files changed, 35 insertions, 4 deletions
diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c index 7515cf0e1805..5db36caf4289 100644 --- a/arch/x86/kernel/vsyscall_64.c +++ b/arch/x86/kernel/vsyscall_64.c | |||
@@ -139,6 +139,19 @@ static int addr_to_vsyscall_nr(unsigned long addr) | |||
139 | return nr; | 139 | return nr; |
140 | } | 140 | } |
141 | 141 | ||
142 | #ifdef CONFIG_SECCOMP | ||
143 | static int vsyscall_seccomp(struct task_struct *tsk, int syscall_nr) | ||
144 | { | ||
145 | if (!seccomp_mode(&tsk->seccomp)) | ||
146 | return 0; | ||
147 | task_pt_regs(tsk)->orig_ax = syscall_nr; | ||
148 | task_pt_regs(tsk)->ax = syscall_nr; | ||
149 | return __secure_computing(syscall_nr); | ||
150 | } | ||
151 | #else | ||
152 | #define vsyscall_seccomp(_tsk, _nr) 0 | ||
153 | #endif | ||
154 | |||
142 | static bool write_ok_or_segv(unsigned long ptr, size_t size) | 155 | static bool write_ok_or_segv(unsigned long ptr, size_t size) |
143 | { | 156 | { |
144 | /* | 157 | /* |
@@ -174,6 +187,7 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
174 | int vsyscall_nr; | 187 | int vsyscall_nr; |
175 | int prev_sig_on_uaccess_error; | 188 | int prev_sig_on_uaccess_error; |
176 | long ret; | 189 | long ret; |
190 | int skip; | ||
177 | 191 | ||
178 | /* | 192 | /* |
179 | * No point in checking CS -- the only way to get here is a user mode | 193 | * No point in checking CS -- the only way to get here is a user mode |
@@ -205,9 +219,6 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
205 | } | 219 | } |
206 | 220 | ||
207 | tsk = current; | 221 | tsk = current; |
208 | if (seccomp_mode(&tsk->seccomp)) | ||
209 | do_exit(SIGKILL); | ||
210 | |||
211 | /* | 222 | /* |
212 | * With a real vsyscall, page faults cause SIGSEGV. We want to | 223 | * With a real vsyscall, page faults cause SIGSEGV. We want to |
213 | * preserve that behavior to make writing exploits harder. | 224 | * preserve that behavior to make writing exploits harder. |
@@ -222,8 +233,13 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
222 | * address 0". | 233 | * address 0". |
223 | */ | 234 | */ |
224 | ret = -EFAULT; | 235 | ret = -EFAULT; |
236 | skip = 0; | ||
225 | switch (vsyscall_nr) { | 237 | switch (vsyscall_nr) { |
226 | case 0: | 238 | case 0: |
239 | skip = vsyscall_seccomp(tsk, __NR_gettimeofday); | ||
240 | if (skip) | ||
241 | break; | ||
242 | |||
227 | if (!write_ok_or_segv(regs->di, sizeof(struct timeval)) || | 243 | if (!write_ok_or_segv(regs->di, sizeof(struct timeval)) || |
228 | !write_ok_or_segv(regs->si, sizeof(struct timezone))) | 244 | !write_ok_or_segv(regs->si, sizeof(struct timezone))) |
229 | break; | 245 | break; |
@@ -234,6 +250,10 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
234 | break; | 250 | break; |
235 | 251 | ||
236 | case 1: | 252 | case 1: |
253 | skip = vsyscall_seccomp(tsk, __NR_time); | ||
254 | if (skip) | ||
255 | break; | ||
256 | |||
237 | if (!write_ok_or_segv(regs->di, sizeof(time_t))) | 257 | if (!write_ok_or_segv(regs->di, sizeof(time_t))) |
238 | break; | 258 | break; |
239 | 259 | ||
@@ -241,6 +261,10 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
241 | break; | 261 | break; |
242 | 262 | ||
243 | case 2: | 263 | case 2: |
264 | skip = vsyscall_seccomp(tsk, __NR_getcpu); | ||
265 | if (skip) | ||
266 | break; | ||
267 | |||
244 | if (!write_ok_or_segv(regs->di, sizeof(unsigned)) || | 268 | if (!write_ok_or_segv(regs->di, sizeof(unsigned)) || |
245 | !write_ok_or_segv(regs->si, sizeof(unsigned))) | 269 | !write_ok_or_segv(regs->si, sizeof(unsigned))) |
246 | break; | 270 | break; |
@@ -253,6 +277,12 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
253 | 277 | ||
254 | current_thread_info()->sig_on_uaccess_error = prev_sig_on_uaccess_error; | 278 | current_thread_info()->sig_on_uaccess_error = prev_sig_on_uaccess_error; |
255 | 279 | ||
280 | if (skip) { | ||
281 | if ((long)regs->ax <= 0L) /* seccomp errno emulation */ | ||
282 | goto do_ret; | ||
283 | goto done; /* seccomp trace/trap */ | ||
284 | } | ||
285 | |||
256 | if (ret == -EFAULT) { | 286 | if (ret == -EFAULT) { |
257 | /* Bad news -- userspace fed a bad pointer to a vsyscall. */ | 287 | /* Bad news -- userspace fed a bad pointer to a vsyscall. */ |
258 | warn_bad_vsyscall(KERN_INFO, regs, | 288 | warn_bad_vsyscall(KERN_INFO, regs, |
@@ -271,10 +301,11 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) | |||
271 | 301 | ||
272 | regs->ax = ret; | 302 | regs->ax = ret; |
273 | 303 | ||
304 | do_ret: | ||
274 | /* Emulate a ret instruction. */ | 305 | /* Emulate a ret instruction. */ |
275 | regs->ip = caller; | 306 | regs->ip = caller; |
276 | regs->sp += 8; | 307 | regs->sp += 8; |
277 | 308 | done: | |
278 | return true; | 309 | return true; |
279 | 310 | ||
280 | sigsegv: | 311 | sigsegv: |