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