diff options
Diffstat (limited to 'arch/x86/kernel')
| -rw-r--r-- | arch/x86/kernel/vsyscall_64.c | 39 | 
1 files changed, 35 insertions, 4 deletions
diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c index 7515cf0e180..5db36caf428 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)  	return nr;  } +#ifdef CONFIG_SECCOMP +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); +} +#else +#define vsyscall_seccomp(_tsk, _nr) 0 +#endif +  static bool write_ok_or_segv(unsigned long ptr, size_t size)  {  	/* @@ -174,6 +187,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 +219,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 +233,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 +250,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 +261,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 +277,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 +301,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:  |