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.c35
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
142static 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
142static bool write_ok_or_segv(unsigned long ptr, size_t size) 151static 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
300do_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 304done:
278 return true; 305 return true;
279 306
280sigsegv: 307sigsegv: