diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c index 18ae83d..c0bafec 100644 --- a/arch/x86/kernel/vsyscall_64.c +++ b/arch/x86/kernel/vsyscall_64.c @@ -139,6 +139,42 @@ static int addr_to_vsyscall_nr(unsigned long addr) return nr; } +/* Copy data to user space, forcing signals on failure. */ +static int copy_to_user_sig(unsigned long dest, const void *src, size_t len) +{ + /* + * This may be the slowest memcpy ever written. We don't really care. + */ + size_t i; + for (i = 0; i < len; i++) { + char __user *user_byte = (char __user *)(dest + i); + if (put_user(((char*)src)[i], user_byte) != 0) { + /* Report full siginfo and context */ + struct task_struct *tsk = current; + siginfo_t info; + memset(&info, 0, sizeof(info)); + info.si_signo = SIGSEGV; + /* + * Could be SEGV_ACCERR -- we don't distinguish it + * correctly. + */ + info.si_code = SEGV_MAPERR; + info.si_addr = user_byte; + /* + * Write fault in user mode. We don't distinguish + * protection fault from no page found. + */ + tsk->thread.error_code = 6; + tsk->thread.cr2 = (unsigned long)user_byte; + tsk->thread.trap_no = 14; + force_sig_info(SIGSEGV, &info, tsk); + return -EFAULT; + } + } + + return 0; +} + bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) { struct task_struct *tsk; @@ -181,10 +217,19 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) switch (vsyscall_nr) { case 0: - ret = sys_gettimeofday( - (struct timeval __user *)regs->di, - (struct timezone __user *)regs->si); + { + struct timeval tv; + do_gettimeofday(&tv); + + if (regs->di && copy_to_user_sig(regs->di, &tv, sizeof(tv))) + goto warn_fault; + if (regs->si && copy_to_user_sig(regs->si, &sys_tz, + sizeof(struct timezone))) + goto warn_fault; + + ret = 0; break; + } case 1: ret = sys_time((time_t __user *)regs->di); @@ -197,19 +242,6 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) break; } - if (ret == -EFAULT) { - /* - * Bad news -- userspace fed a bad pointer to a vsyscall. - * - * With a real vsyscall, that would have caused SIGSEGV. - * To make writing reliable exploits using the emulated - * vsyscalls harder, generate SIGSEGV here as well. - */ - warn_bad_vsyscall(KERN_INFO, regs, - "vsyscall fault (exploit attempt?)"); - goto sigsegv; - } - regs->ax = ret; /* Emulate a ret instruction. */ @@ -221,6 +253,19 @@ bool emulate_vsyscall(struct pt_regs *regs, unsigned long address) sigsegv: force_sig(SIGSEGV, current); return true; + +warn_fault: + /* + * Bad news -- userspace fed a bad pointer to a vsyscall. + * + * With a real vsyscall, that would have caused SIGSEGV. + * To make writing reliable exploits using the emulated + * vsyscalls harder, generate SIGSEGV here as well. + */ + + warn_bad_vsyscall(KERN_INFO, regs, + "vsyscall fault (exploit attempt?)"); + return true; } /*