aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/vsyscall_64.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/kernel/vsyscall_64.c')
-rw-r--r--arch/x86/kernel/vsyscall_64.c39
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
143static 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
142static bool write_ok_or_segv(unsigned long ptr, size_t size) 155static 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
304do_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 308done:
278 return true; 309 return true;
279 310
280sigsegv: 311sigsegv: