|
@@ -139,6 +139,15 @@ static int addr_to_vsyscall_nr(unsigned long addr)
|
|
|
return nr;
|
|
|
}
|
|
|
|
|
|
+static int vsyscall_seccomp(struct task_struct *tsk, int syscall_nr)
|
|
|
+{
|
|
|
+ if (!seccomp_mode(&tsk->seccomp))
|
|
|
+ return 0;
|
|
|
+ task_pt_regs(tsk)->orig_ax = syscall_nr;
|
|
|
+ task_pt_regs(tsk)->ax = syscall_nr;
|
|
|
+ return __secure_computing(syscall_nr);
|
|
|
+}
|
|
|
+
|
|
|
static bool write_ok_or_segv(unsigned long ptr, size_t size)
|
|
|
{
|
|
|
/*
|
|
@@ -174,6 +183,7 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
|
|
|
int vsyscall_nr;
|
|
|
int prev_sig_on_uaccess_error;
|
|
|
long ret;
|
|
|
+ int skip;
|
|
|
|
|
|
/*
|
|
|
* 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)
|
|
|
}
|
|
|
|
|
|
tsk = current;
|
|
|
- if (seccomp_mode(&tsk->seccomp))
|
|
|
- do_exit(SIGKILL);
|
|
|
-
|
|
|
/*
|
|
|
* With a real vsyscall, page faults cause SIGSEGV. We want to
|
|
|
* preserve that behavior to make writing exploits harder.
|
|
@@ -222,8 +229,13 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
|
|
|
* address 0".
|
|
|
*/
|
|
|
ret = -EFAULT;
|
|
|
+ skip = 0;
|
|
|
switch (vsyscall_nr) {
|
|
|
case 0:
|
|
|
+ skip = vsyscall_seccomp(tsk, __NR_gettimeofday);
|
|
|
+ if (skip)
|
|
|
+ break;
|
|
|
+
|
|
|
if (!write_ok_or_segv(regs->di, sizeof(struct timeval)) ||
|
|
|
!write_ok_or_segv(regs->si, sizeof(struct timezone)))
|
|
|
break;
|
|
@@ -234,6 +246,10 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
|
|
|
break;
|
|
|
|
|
|
case 1:
|
|
|
+ skip = vsyscall_seccomp(tsk, __NR_time);
|
|
|
+ if (skip)
|
|
|
+ break;
|
|
|
+
|
|
|
if (!write_ok_or_segv(regs->di, sizeof(time_t)))
|
|
|
break;
|
|
|
|
|
@@ -241,6 +257,10 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
|
|
|
break;
|
|
|
|
|
|
case 2:
|
|
|
+ skip = vsyscall_seccomp(tsk, __NR_getcpu);
|
|
|
+ if (skip)
|
|
|
+ break;
|
|
|
+
|
|
|
if (!write_ok_or_segv(regs->di, sizeof(unsigned)) ||
|
|
|
!write_ok_or_segv(regs->si, sizeof(unsigned)))
|
|
|
break;
|
|
@@ -253,6 +273,12 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
|
|
|
|
|
|
current_thread_info()->sig_on_uaccess_error = prev_sig_on_uaccess_error;
|
|
|
|
|
|
+ if (skip) {
|
|
|
+ if ((long)regs->ax <= 0L) /* seccomp errno emulation */
|
|
|
+ goto do_ret;
|
|
|
+ goto done; /* seccomp trace/trap */
|
|
|
+ }
|
|
|
+
|
|
|
if (ret == -EFAULT) {
|
|
|
/* Bad news -- userspace fed a bad pointer to a vsyscall. */
|
|
|
warn_bad_vsyscall(KERN_INFO, regs,
|
|
@@ -271,10 +297,11 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
|
|
|
|
|
|
regs->ax = ret;
|
|
|
|
|
|
+do_ret:
|
|
|
/* Emulate a ret instruction. */
|
|
|
regs->ip = caller;
|
|
|
regs->sp += 8;
|
|
|
-
|
|
|
+done:
|
|
|
return true;
|
|
|
|
|
|
sigsegv:
|