x86/entry: Convert Overflow exception to IDTENTRY
[sfrench/cifs-2.6.git] / arch / x86 / kernel / traps.c
index 4cc541051994d53b63de16dbfcf4bee22bd47ebc..b522e2aa9e9afb442cca26efa87b72c398b42edd 100644 (file)
@@ -201,9 +201,36 @@ static void do_error_trap(struct pt_regs *regs, long error_code, char *str,
                        NOTIFY_STOP) {
                cond_local_irq_enable(regs);
                do_trap(trapnr, signr, str, regs, error_code, sicode, addr);
+               cond_local_irq_disable(regs);
        }
 }
 
+/*
+ * Posix requires to provide the address of the faulting instruction for
+ * SIGILL (#UD) and SIGFPE (#DE) in the si_addr member of siginfo_t.
+ *
+ * This address is usually regs->ip, but when an uprobe moved the code out
+ * of line then regs->ip points to the XOL code which would confuse
+ * anything which analyzes the fault address vs. the unmodified binary. If
+ * a trap happened in XOL code then uprobe maps regs->ip back to the
+ * original instruction address.
+ */
+static __always_inline void __user *error_get_trap_addr(struct pt_regs *regs)
+{
+       return (void __user *)uprobe_get_trap_addr(regs);
+}
+
+DEFINE_IDTENTRY(exc_divide_error)
+{
+       do_error_trap(regs, 0, "divide_error", X86_TRAP_DE, SIGFPE,
+                     FPE_INTDIV, error_get_trap_addr(regs));
+}
+
+DEFINE_IDTENTRY(exc_overflow)
+{
+       do_error_trap(regs, 0, "overflow", X86_TRAP_OF, SIGSEGV, 0, NULL);
+}
+
 #define IP ((void __user *)uprobe_get_trap_addr(regs))
 #define DO_ERROR(trapnr, signr, sicode, addr, str, name)                  \
 dotraplinkage void do_##name(struct pt_regs *regs, long error_code)       \
@@ -211,8 +238,6 @@ dotraplinkage void do_##name(struct pt_regs *regs, long error_code)    \
        do_error_trap(regs, error_code, str, trapnr, signr, sicode, addr); \
 }
 
-DO_ERROR(X86_TRAP_DE,     SIGFPE,  FPE_INTDIV,   IP, "divide error",        divide_error)
-DO_ERROR(X86_TRAP_OF,     SIGSEGV,          0, NULL, "overflow",            overflow)
 DO_ERROR(X86_TRAP_UD,     SIGILL,  ILL_ILLOPN,   IP, "invalid opcode",      invalid_op)
 DO_ERROR(X86_TRAP_OLD_MF, SIGFPE,           0, NULL, "coprocessor segment overrun", coprocessor_segment_overrun)
 DO_ERROR(X86_TRAP_TS,     SIGSEGV,          0, NULL, "invalid TSS",         invalid_TSS)
@@ -299,6 +324,7 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code, unsign
                regs->ip == (unsigned long)native_irq_return_iret)
        {
                struct pt_regs *gpregs = (struct pt_regs *)this_cpu_read(cpu_tss_rw.x86_tss.sp0) - 1;
+               unsigned long *p = (unsigned long *)regs->sp;
 
                /*
                 * regs->sp points to the failing IRET frame on the
@@ -306,7 +332,11 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code, unsign
                 * in gpregs->ss through gpregs->ip.
                 *
                 */
-               memmove(&gpregs->ip, (void *)regs->sp, 5*8);
+               gpregs->ip      = p[0];
+               gpregs->cs      = p[1];
+               gpregs->flags   = p[2];
+               gpregs->sp      = p[3];
+               gpregs->ss      = p[4];
                gpregs->orig_ax = 0;  /* Missing (lost) #GP error code */
 
                /*
@@ -392,6 +422,8 @@ dotraplinkage void do_bounds(struct pt_regs *regs, long error_code)
                die("bounds", regs, error_code);
 
        do_trap(X86_TRAP_BR, SIGSEGV, "bounds", regs, error_code, 0, NULL);
+
+       cond_local_irq_disable(regs);
 }
 
 enum kernel_gp_hint {
@@ -451,12 +483,13 @@ dotraplinkage void do_general_protection(struct pt_regs *regs, long error_code)
 
        if (static_cpu_has(X86_FEATURE_UMIP)) {
                if (user_mode(regs) && fixup_umip_exception(regs))
-                       return;
+                       goto exit;
        }
 
        if (v8086_mode(regs)) {
                local_irq_enable();
                handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code);
+               local_irq_disable();
                return;
        }
 
@@ -468,12 +501,11 @@ dotraplinkage void do_general_protection(struct pt_regs *regs, long error_code)
 
                show_signal(tsk, SIGSEGV, "", desc, regs, error_code);
                force_sig(SIGSEGV);
-
-               return;
+               goto exit;
        }
 
        if (fixup_exception(regs, X86_TRAP_GP, error_code, 0))
-               return;
+               goto exit;
 
        tsk->thread.error_code = error_code;
        tsk->thread.trap_nr = X86_TRAP_GP;
@@ -485,11 +517,11 @@ dotraplinkage void do_general_protection(struct pt_regs *regs, long error_code)
        if (!preemptible() &&
            kprobe_running() &&
            kprobe_fault_handler(regs, X86_TRAP_GP))
-               return;
+               goto exit;
 
        ret = notify_die(DIE_GPF, desc, regs, error_code, X86_TRAP_GP, SIGSEGV);
        if (ret == NOTIFY_STOP)
-               return;
+               goto exit;
 
        if (error_code)
                snprintf(desc, sizeof(desc), "segment-related " GPFSTR);
@@ -511,6 +543,8 @@ dotraplinkage void do_general_protection(struct pt_regs *regs, long error_code)
 
        die_addr(desc, regs, error_code, gp_addr);
 
+exit:
+       cond_local_irq_disable(regs);
 }
 NOKPROBE_SYMBOL(do_general_protection);
 
@@ -559,21 +593,20 @@ NOKPROBE_SYMBOL(do_int3);
  * to switch to the normal thread stack if the interrupted code was in
  * user mode. The actual stack switch is done in entry_64.S
  */
-asmlinkage __visible notrace struct pt_regs *sync_regs(struct pt_regs *eregs)
+asmlinkage __visible noinstr struct pt_regs *sync_regs(struct pt_regs *eregs)
 {
        struct pt_regs *regs = (struct pt_regs *)this_cpu_read(cpu_current_top_of_stack) - 1;
        if (regs != eregs)
                *regs = *eregs;
        return regs;
 }
-NOKPROBE_SYMBOL(sync_regs);
 
 struct bad_iret_stack {
        void *error_entry_ret;
        struct pt_regs regs;
 };
 
-asmlinkage __visible notrace
+asmlinkage __visible noinstr
 struct bad_iret_stack *fixup_bad_iret(struct bad_iret_stack *s)
 {
        /*
@@ -584,19 +617,21 @@ struct bad_iret_stack *fixup_bad_iret(struct bad_iret_stack *s)
         * just below the IRET frame) and we want to pretend that the
         * exception came from the IRET target.
         */
-       struct bad_iret_stack *new_stack =
-               (struct bad_iret_stack *)this_cpu_read(cpu_tss_rw.x86_tss.sp0) - 1;
+       struct bad_iret_stack tmp, *new_stack =
+               (struct bad_iret_stack *)__this_cpu_read(cpu_tss_rw.x86_tss.sp0) - 1;
 
-       /* Copy the IRET target to the new stack. */
-       memmove(&new_stack->regs.ip, (void *)s->regs.sp, 5*8);
+       /* Copy the IRET target to the temporary storage. */
+       memcpy(&tmp.regs.ip, (void *)s->regs.sp, 5*8);
 
        /* Copy the remainder of the stack from the current stack. */
-       memmove(new_stack, s, offsetof(struct bad_iret_stack, regs.ip));
+       memcpy(&tmp, s, offsetof(struct bad_iret_stack, regs.ip));
+
+       /* Update the entry stack */
+       memcpy(new_stack, &tmp, sizeof(tmp));
 
        BUG_ON(!user_mode(&new_stack->regs));
        return new_stack;
 }
-NOKPROBE_SYMBOL(fixup_bad_iret);
 #endif
 
 static bool is_sysenter_singlestep(struct pt_regs *regs)
@@ -767,7 +802,7 @@ static void math_error(struct pt_regs *regs, int error_code, int trapnr)
 
        if (!user_mode(regs)) {
                if (fixup_exception(regs, trapnr, error_code, 0))
-                       return;
+                       goto exit;
 
                task->thread.error_code = error_code;
                task->thread.trap_nr = trapnr;
@@ -775,7 +810,7 @@ static void math_error(struct pt_regs *regs, int error_code, int trapnr)
                if (notify_die(DIE_TRAP, str, regs, error_code,
                                        trapnr, SIGFPE) != NOTIFY_STOP)
                        die(str, regs, error_code);
-               return;
+               goto exit;
        }
 
        /*
@@ -789,10 +824,12 @@ static void math_error(struct pt_regs *regs, int error_code, int trapnr)
        si_code = fpu__exception_code(fpu, trapnr);
        /* Retry when we get spurious exceptions: */
        if (!si_code)
-               return;
+               goto exit;
 
        force_sig_fault(SIGFPE, si_code,
                        (void __user *)uprobe_get_trap_addr(regs));
+exit:
+       cond_local_irq_disable(regs);
 }
 
 dotraplinkage void do_coprocessor_error(struct pt_regs *regs, long error_code)
@@ -847,6 +884,8 @@ do_device_not_available(struct pt_regs *regs, long error_code)
 
                info.regs = regs;
                math_emulate(&info);
+
+               cond_local_irq_disable(regs);
                return;
        }
 #endif
@@ -877,6 +916,7 @@ dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code)
                do_trap(X86_TRAP_IRET, SIGILL, "iret exception", regs, error_code,
                        ILL_BADSTK, (void __user *)NULL);
        }
+       local_irq_disable();
 }
 #endif