Commit 5651721e authored by Will Drewry's avatar Will Drewry Committed by Linus Torvalds

x86/vsyscall: allow seccomp filter in vsyscall=emulate

If a seccomp filter program is installed, older static binaries and
distributions with older libc implementations (glibc 2.13 and earlier)
that rely on vsyscall use will be terminated regardless of the filter
program policy when executing time, gettimeofday, or getcpu.  This is
only the case when vsyscall emulation is in use (vsyscall=emulate is the
default).

This patch emulates system call entry inside a vsyscall=emulate by
populating regs->ax and regs->orig_ax with the system call number prior
to calling into seccomp such that all seccomp-dependencies function
normally.  Additionally, system call return behavior is emulated in line
with other vsyscall entrypoints for the trace/trap cases.

[ v2: fixed ip and sp on SECCOMP_RET_TRAP/TRACE (thanks to luto@mit.edu) ]
Reported-and-tested-by: default avatarOwen Kibel <qmewlo@gmail.com>
Signed-off-by: default avatarWill Drewry <wad@chromium.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent ac7d181e
...@@ -139,6 +139,15 @@ static int addr_to_vsyscall_nr(unsigned long addr) ...@@ -139,6 +139,15 @@ static int addr_to_vsyscall_nr(unsigned long addr)
return nr; 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) 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) ...@@ -174,6 +183,7 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
int vsyscall_nr; int vsyscall_nr;
int prev_sig_on_uaccess_error; int prev_sig_on_uaccess_error;
long ret; long ret;
int skip;
/* /*
* No point in checking CS -- the only way to get here is a user mode * 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,9 +215,6 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
} }
tsk = current; tsk = current;
if (seccomp_mode(&tsk->seccomp))
do_exit(SIGKILL);
/* /*
* With a real vsyscall, page faults cause SIGSEGV. We want to * With a real vsyscall, page faults cause SIGSEGV. We want to
* preserve that behavior to make writing exploits harder. * preserve that behavior to make writing exploits harder.
...@@ -222,8 +229,13 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) ...@@ -222,8 +229,13 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
* address 0". * address 0".
*/ */
ret = -EFAULT; ret = -EFAULT;
skip = 0;
switch (vsyscall_nr) { switch (vsyscall_nr) {
case 0: case 0:
skip = vsyscall_seccomp(tsk, __NR_gettimeofday);
if (skip)
break;
if (!write_ok_or_segv(regs->di, sizeof(struct timeval)) || if (!write_ok_or_segv(regs->di, sizeof(struct timeval)) ||
!write_ok_or_segv(regs->si, sizeof(struct timezone))) !write_ok_or_segv(regs->si, sizeof(struct timezone)))
break; break;
...@@ -234,6 +246,10 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) ...@@ -234,6 +246,10 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
break; break;
case 1: case 1:
skip = vsyscall_seccomp(tsk, __NR_time);
if (skip)
break;
if (!write_ok_or_segv(regs->di, sizeof(time_t))) if (!write_ok_or_segv(regs->di, sizeof(time_t)))
break; break;
...@@ -241,6 +257,10 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) ...@@ -241,6 +257,10 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
break; break;
case 2: case 2:
skip = vsyscall_seccomp(tsk, __NR_getcpu);
if (skip)
break;
if (!write_ok_or_segv(regs->di, sizeof(unsigned)) || if (!write_ok_or_segv(regs->di, sizeof(unsigned)) ||
!write_ok_or_segv(regs->si, sizeof(unsigned))) !write_ok_or_segv(regs->si, sizeof(unsigned)))
break; break;
...@@ -253,6 +273,12 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) ...@@ -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; 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) { if (ret == -EFAULT) {
/* Bad news -- userspace fed a bad pointer to a vsyscall. */ /* Bad news -- userspace fed a bad pointer to a vsyscall. */
warn_bad_vsyscall(KERN_INFO, regs, warn_bad_vsyscall(KERN_INFO, regs,
...@@ -271,10 +297,11 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) ...@@ -271,10 +297,11 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
regs->ax = ret; regs->ax = ret;
do_ret:
/* Emulate a ret instruction. */ /* Emulate a ret instruction. */
regs->ip = caller; regs->ip = caller;
regs->sp += 8; regs->sp += 8;
done:
return true; return true;
sigsegv: sigsegv:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment