Skip to content

Commit

Permalink
x86/vsyscall: allow seccomp filter in vsyscall=emulate
Browse files Browse the repository at this point in the history
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: Owen Kibel <qmewlo@gmail.com>
Signed-off-by: Will Drewry <wad@chromium.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Will Drewry authored and Linus Torvalds committed Jul 13, 2012
1 parent ac7d181 commit 5651721
Showing 1 changed file with 31 additions and 4 deletions.
35 changes: 31 additions & 4 deletions arch/x86/kernel/vsyscall_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
/*
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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;
Expand All @@ -234,13 +246,21 @@ 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;

ret = sys_time((time_t __user *)regs->di);
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;
Expand All @@ -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,
Expand All @@ -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:
Expand Down

0 comments on commit 5651721

Please sign in to comment.