return nr;
 }
 
+static bool write_ok_or_segv(unsigned long ptr, size_t size)
+{
+       /*
+        * XXX: if access_ok, get_user, and put_user handled
+        * sig_on_uaccess_error, this could go away.
+        */
+
+       if (!access_ok(VERIFY_WRITE, (void __user *)ptr, size)) {
+               siginfo_t info;
+               struct thread_struct *thread = ¤t->thread;
+
+               thread->error_code      = 6;  /* user fault, no page, write */
+               thread->cr2             = ptr;
+               thread->trap_no         = 14;
+
+               memset(&info, 0, sizeof(info));
+               info.si_signo           = SIGSEGV;
+               info.si_errno           = 0;
+               info.si_code            = SEGV_MAPERR;
+               info.si_addr            = (void __user *)ptr;
+
+               force_sig_info(SIGSEGV, &info, current);
+               return false;
+       } else {
+               return true;
+       }
+}
+
 bool emulate_vsyscall(struct pt_regs *regs, unsigned long address)
 {
        struct task_struct *tsk;
        unsigned long caller;
        int vsyscall_nr;
+       int prev_sig_on_uaccess_error;
        long ret;
 
        /*
        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.
+        */
+       prev_sig_on_uaccess_error = current_thread_info()->sig_on_uaccess_error;
+       current_thread_info()->sig_on_uaccess_error = 1;
+
+       /*
+        * 0 is a valid user pointer (in the access_ok sense) on 32-bit and
+        * 64-bit, so we don't need to special-case it here.  For all the
+        * vsyscalls, 0 means "don't write anything" not "write it at
+        * address 0".
+        */
+       ret = -EFAULT;
        switch (vsyscall_nr) {
        case 0:
+               if (!write_ok_or_segv(regs->di, sizeof(struct timeval)) ||
+                   !write_ok_or_segv(regs->si, sizeof(struct timezone)))
+                       break;
+
                ret = sys_gettimeofday(
                        (struct timeval __user *)regs->di,
                        (struct timezone __user *)regs->si);
                break;
 
        case 1:
+               if (!write_ok_or_segv(regs->di, sizeof(time_t)))
+                       break;
+
                ret = sys_time((time_t __user *)regs->di);
                break;
 
        case 2:
+               if (!write_ok_or_segv(regs->di, sizeof(unsigned)) ||
+                   !write_ok_or_segv(regs->si, sizeof(unsigned)))
+                       break;
+
                ret = sys_getcpu((unsigned __user *)regs->di,
                                 (unsigned __user *)regs->si,
                                 0);
                break;
        }
 
+       current_thread_info()->sig_on_uaccess_error = prev_sig_on_uaccess_error;
+
        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.
-                */
+               /* Bad news -- userspace fed a bad pointer to a vsyscall. */
                warn_bad_vsyscall(KERN_INFO, regs,
                                  "vsyscall fault (exploit attempt?)");
-               goto sigsegv;
+
+               /*
+                * If we failed to generate a signal for any reason,
+                * generate one here.  (This should be impossible.)
+                */
+               if (WARN_ON_ONCE(!sigismember(&tsk->pending.signal, SIGBUS) &&
+                                !sigismember(&tsk->pending.signal, SIGSEGV)))
+                       goto sigsegv;
+
+               return true;  /* Don't emulate the ret. */
        }
 
        regs->ax = ret;
 
 
 static noinline void
 no_context(struct pt_regs *regs, unsigned long error_code,
-          unsigned long address)
+          unsigned long address, int signal, int si_code)
 {
        struct task_struct *tsk = current;
        unsigned long *stackend;
        int sig;
 
        /* Are we prepared to handle this kernel fault? */
-       if (fixup_exception(regs))
+       if (fixup_exception(regs)) {
+               if (current_thread_info()->sig_on_uaccess_error && signal) {
+                       tsk->thread.trap_no = 14;
+                       tsk->thread.error_code = error_code | PF_USER;
+                       tsk->thread.cr2 = address;
+
+                       /* XXX: hwpoison faults will set the wrong code. */
+                       force_sig_info_fault(signal, si_code, address, tsk, 0);
+               }
                return;
+       }
 
        /*
         * 32-bit:
        if (is_f00f_bug(regs, address))
                return;
 
-       no_context(regs, error_code, address);
+       no_context(regs, error_code, address, SIGSEGV, si_code);
 }
 
 static noinline void
 
        /* Kernel mode? Handle exceptions or die: */
        if (!(error_code & PF_USER)) {
-               no_context(regs, error_code, address);
+               no_context(regs, error_code, address, SIGBUS, BUS_ADRERR);
                return;
        }
 
                if (!(fault & VM_FAULT_RETRY))
                        up_read(¤t->mm->mmap_sem);
                if (!(error_code & PF_USER))
-                       no_context(regs, error_code, address);
+                       no_context(regs, error_code, address, 0, 0);
                return 1;
        }
        if (!(fault & VM_FAULT_ERROR))
                /* Kernel mode? Handle exceptions or die: */
                if (!(error_code & PF_USER)) {
                        up_read(¤t->mm->mmap_sem);
-                       no_context(regs, error_code, address);
+                       no_context(regs, error_code, address,
+                                  SIGSEGV, SEGV_MAPERR);
                        return 1;
                }