[PATCH] i386: Disallow kprobes on NMI handlers
authorFernando Luis Vázquez Cao <fernando@oss.ntt.co.jp>
Tue, 26 Sep 2006 08:52:36 +0000 (10:52 +0200)
committerAndi Kleen <andi@basil.nowhere.org>
Tue, 26 Sep 2006 08:52:36 +0000 (10:52 +0200)
A kprobe executes IRET early and that could cause NMI recursion and stack
corruption.

Note: This problem was originally spotted and solved by Andi Kleen in the
x86_64 architecture. This patch is an adaption of his patch for i386.

AK: Merged with current code which was a bit different.
AK: Removed printk in nmi handler that shouldn't be there in the first time
AK: Added missing include.
AK: added KPROBES_END

Signed-off-by: Fernando Vazquez <fernando@intellilink.co.jp>
Signed-off-by: Andi Kleen <ak@suse.de>
arch/i386/kernel/entry.S
arch/i386/kernel/nmi.c
arch/i386/kernel/traps.c

index dede506..0928f70 100644 (file)
@@ -729,7 +729,7 @@ KPROBE_END(debug)
  * check whether we got an NMI on the debug path where the debug
  * fault happened on the sysenter path.
  */
-ENTRY(nmi)
+KPROBE_ENTRY(nmi)
        RING0_INT_FRAME
        pushl %eax
        CFI_ADJUST_CFA_OFFSET 4
@@ -805,6 +805,7 @@ nmi_16bit_stack:
        .align 4
        .long 1b,iret_exc
 .previous
+KPROBE_END(nmi)
 
 KPROBE_ENTRY(int3)
        RING0_INT_FRAME
index 7b9a053..dbda706 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/sysctl.h>
 #include <linux/percpu.h>
 #include <linux/dmi.h>
+#include <linux/kprobes.h>
 
 #include <asm/smp.h>
 #include <asm/nmi.h>
@@ -882,7 +883,7 @@ EXPORT_SYMBOL(touch_nmi_watchdog);
 
 extern void die_nmi(struct pt_regs *, const char *msg);
 
-int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason)
+__kprobes int nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
 {
 
        /*
@@ -962,8 +963,7 @@ int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason)
                         * This matches the old behaviour.
                         */
                        rc = 1;
-               } else
-                       printk(KERN_WARNING "Unknown enabled NMI hardware?!\n");
+               }
        }
 done:
        return rc;
index 00d643f..5c0f496 100644 (file)
@@ -689,7 +689,8 @@ gp_in_kernel:
        }
 }
 
-static void mem_parity_error(unsigned char reason, struct pt_regs * regs)
+static __kprobes void
+mem_parity_error(unsigned char reason, struct pt_regs * regs)
 {
        printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x on "
                "CPU %d.\n", reason, smp_processor_id());
@@ -704,7 +705,8 @@ static void mem_parity_error(unsigned char reason, struct pt_regs * regs)
        clear_mem_error(reason);
 }
 
-static void io_check_error(unsigned char reason, struct pt_regs * regs)
+static __kprobes void
+io_check_error(unsigned char reason, struct pt_regs * regs)
 {
        unsigned long i;
 
@@ -720,7 +722,8 @@ static void io_check_error(unsigned char reason, struct pt_regs * regs)
        outb(reason, 0x61);
 }
 
-static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs)
+static __kprobes void
+unknown_nmi_error(unsigned char reason, struct pt_regs * regs)
 {
 #ifdef CONFIG_MCA
        /* Might actually be able to figure out what the guilty party
@@ -741,7 +744,7 @@ static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs)
 
 static DEFINE_SPINLOCK(nmi_print_lock);
 
-void die_nmi (struct pt_regs *regs, const char *msg)
+void __kprobes die_nmi(struct pt_regs *regs, const char *msg)
 {
        if (notify_die(DIE_NMIWATCHDOG, msg, regs, 0, 2, SIGINT) ==
            NOTIFY_STOP)
@@ -773,7 +776,7 @@ void die_nmi (struct pt_regs *regs, const char *msg)
        do_exit(SIGSEGV);
 }
 
-static void default_do_nmi(struct pt_regs * regs)
+static __kprobes void default_do_nmi(struct pt_regs * regs)
 {
        unsigned char reason = 0;
 
@@ -811,7 +814,7 @@ static void default_do_nmi(struct pt_regs * regs)
        reassert_nmi();
 }
 
-fastcall void do_nmi(struct pt_regs * regs, long error_code)
+fastcall __kprobes void do_nmi(struct pt_regs * regs, long error_code)
 {
        int cpu;