Commit 06039754 authored by Fernando Luis Vzquez Cao's avatar Fernando Luis Vzquez Cao Committed by Andi Kleen

[PATCH] i386: Disallow kprobes on NMI handlers

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: default avatarFernando Vazquez <fernando@intellilink.co.jp>
Signed-off-by: default avatarAndi Kleen <ak@suse.de>
parent 6f6b1e04
...@@ -729,7 +729,7 @@ KPROBE_END(debug) ...@@ -729,7 +729,7 @@ KPROBE_END(debug)
* check whether we got an NMI on the debug path where the debug * check whether we got an NMI on the debug path where the debug
* fault happened on the sysenter path. * fault happened on the sysenter path.
*/ */
ENTRY(nmi) KPROBE_ENTRY(nmi)
RING0_INT_FRAME RING0_INT_FRAME
pushl %eax pushl %eax
CFI_ADJUST_CFA_OFFSET 4 CFI_ADJUST_CFA_OFFSET 4
...@@ -805,6 +805,7 @@ nmi_16bit_stack: ...@@ -805,6 +805,7 @@ nmi_16bit_stack:
.align 4 .align 4
.long 1b,iret_exc .long 1b,iret_exc
.previous .previous
KPROBE_END(nmi)
KPROBE_ENTRY(int3) KPROBE_ENTRY(int3)
RING0_INT_FRAME RING0_INT_FRAME
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/kprobes.h>
#include <asm/smp.h> #include <asm/smp.h>
#include <asm/nmi.h> #include <asm/nmi.h>
...@@ -882,7 +883,7 @@ EXPORT_SYMBOL(touch_nmi_watchdog); ...@@ -882,7 +883,7 @@ EXPORT_SYMBOL(touch_nmi_watchdog);
extern void die_nmi(struct pt_regs *, const char *msg); 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) ...@@ -962,8 +963,7 @@ int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason)
* This matches the old behaviour. * This matches the old behaviour.
*/ */
rc = 1; rc = 1;
} else }
printk(KERN_WARNING "Unknown enabled NMI hardware?!\n");
} }
done: done:
return rc; return rc;
......
...@@ -689,7 +689,8 @@ fastcall void __kprobes do_general_protection(struct pt_regs * regs, ...@@ -689,7 +689,8 @@ fastcall void __kprobes do_general_protection(struct pt_regs * regs,
} }
} }
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 " printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x on "
"CPU %d.\n", reason, smp_processor_id()); "CPU %d.\n", reason, smp_processor_id());
...@@ -704,7 +705,8 @@ static void mem_parity_error(unsigned char reason, struct pt_regs * regs) ...@@ -704,7 +705,8 @@ static void mem_parity_error(unsigned char reason, struct pt_regs * regs)
clear_mem_error(reason); 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; unsigned long i;
...@@ -720,7 +722,8 @@ static void io_check_error(unsigned char reason, struct pt_regs * regs) ...@@ -720,7 +722,8 @@ static void io_check_error(unsigned char reason, struct pt_regs * regs)
outb(reason, 0x61); 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 #ifdef CONFIG_MCA
/* Might actually be able to figure out what the guilty party /* 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) ...@@ -741,7 +744,7 @@ static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs)
static DEFINE_SPINLOCK(nmi_print_lock); 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) == if (notify_die(DIE_NMIWATCHDOG, msg, regs, 0, 2, SIGINT) ==
NOTIFY_STOP) NOTIFY_STOP)
...@@ -773,7 +776,7 @@ void die_nmi (struct pt_regs *regs, const char *msg) ...@@ -773,7 +776,7 @@ void die_nmi (struct pt_regs *regs, const char *msg)
do_exit(SIGSEGV); 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; unsigned char reason = 0;
...@@ -811,7 +814,7 @@ static void default_do_nmi(struct pt_regs * regs) ...@@ -811,7 +814,7 @@ static void default_do_nmi(struct pt_regs * regs)
reassert_nmi(); 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; int cpu;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment