Commit eddb6fb9 authored by Andi Kleen's avatar Andi Kleen Committed by Linus Torvalds

[PATCH] x86_64: Disallow kprobes on NMI handlers

A kprobe executes IRET early and that could cause NMI recursion
and stack corruption.
Signed-off-by: default avatarAndi Kleen <ak@suse.de>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 2f019425
...@@ -922,7 +922,7 @@ KPROBE_ENTRY(debug) ...@@ -922,7 +922,7 @@ KPROBE_ENTRY(debug)
.previous .text .previous .text
/* runs on exception stack */ /* runs on exception stack */
ENTRY(nmi) KPROBE_ENTRY(nmi)
INTR_FRAME INTR_FRAME
pushq $-1 pushq $-1
CFI_ADJUST_CFA_OFFSET 8 CFI_ADJUST_CFA_OFFSET 8
...@@ -969,6 +969,7 @@ paranoid_schedule: ...@@ -969,6 +969,7 @@ paranoid_schedule:
cli cli
jmp paranoid_userspace jmp paranoid_userspace
CFI_ENDPROC CFI_ENDPROC
.previous .text
KPROBE_ENTRY(int3) KPROBE_ENTRY(int3)
INTR_FRAME INTR_FRAME
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/sysdev.h> #include <linux/sysdev.h>
#include <linux/nmi.h> #include <linux/nmi.h>
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include <linux/kprobes.h>
#include <asm/smp.h> #include <asm/smp.h>
#include <asm/mtrr.h> #include <asm/mtrr.h>
...@@ -468,7 +469,7 @@ void touch_nmi_watchdog (void) ...@@ -468,7 +469,7 @@ void touch_nmi_watchdog (void)
touch_softlockup_watchdog(); touch_softlockup_watchdog();
} }
void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason) void __kprobes nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
{ {
int sum; int sum;
int touched = 0; int touched = 0;
...@@ -512,14 +513,14 @@ void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason) ...@@ -512,14 +513,14 @@ void nmi_watchdog_tick (struct pt_regs * regs, unsigned reason)
} }
} }
static int dummy_nmi_callback(struct pt_regs * regs, int cpu) static __kprobes int dummy_nmi_callback(struct pt_regs * regs, int cpu)
{ {
return 0; return 0;
} }
static nmi_callback_t nmi_callback = dummy_nmi_callback; static nmi_callback_t nmi_callback = dummy_nmi_callback;
asmlinkage void do_nmi(struct pt_regs * regs, long error_code) asmlinkage __kprobes void do_nmi(struct pt_regs * regs, long error_code)
{ {
int cpu = safe_smp_processor_id(); int cpu = safe_smp_processor_id();
......
...@@ -372,7 +372,7 @@ void out_of_line_bug(void) ...@@ -372,7 +372,7 @@ void out_of_line_bug(void)
static DEFINE_SPINLOCK(die_lock); static DEFINE_SPINLOCK(die_lock);
static int die_owner = -1; static int die_owner = -1;
unsigned long oops_begin(void) unsigned __kprobes long oops_begin(void)
{ {
int cpu = safe_smp_processor_id(); int cpu = safe_smp_processor_id();
unsigned long flags; unsigned long flags;
...@@ -391,7 +391,7 @@ unsigned long oops_begin(void) ...@@ -391,7 +391,7 @@ unsigned long oops_begin(void)
return flags; return flags;
} }
void oops_end(unsigned long flags) void __kprobes oops_end(unsigned long flags)
{ {
die_owner = -1; die_owner = -1;
bust_spinlocks(0); bust_spinlocks(0);
...@@ -400,7 +400,7 @@ void oops_end(unsigned long flags) ...@@ -400,7 +400,7 @@ void oops_end(unsigned long flags)
panic("Oops"); panic("Oops");
} }
void __die(const char * str, struct pt_regs * regs, long err) void __kprobes __die(const char * str, struct pt_regs * regs, long err)
{ {
static int die_counter; static int die_counter;
printk(KERN_EMERG "%s: %04lx [%u] ", str, err & 0xffff,++die_counter); printk(KERN_EMERG "%s: %04lx [%u] ", str, err & 0xffff,++die_counter);
...@@ -432,7 +432,7 @@ void die(const char * str, struct pt_regs * regs, long err) ...@@ -432,7 +432,7 @@ void die(const char * str, struct pt_regs * regs, long err)
do_exit(SIGSEGV); do_exit(SIGSEGV);
} }
void die_nmi(char *str, struct pt_regs *regs) void __kprobes die_nmi(char *str, struct pt_regs *regs)
{ {
unsigned long flags = oops_begin(); unsigned long flags = oops_begin();
...@@ -575,7 +575,8 @@ asmlinkage void __kprobes do_general_protection(struct pt_regs * regs, ...@@ -575,7 +575,8 @@ asmlinkage 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("Uhhuh. NMI received. Dazed and confused, but trying to continue\n"); printk("Uhhuh. NMI received. Dazed and confused, but trying to continue\n");
printk("You probably have a hardware problem with your RAM chips\n"); printk("You probably have a hardware problem with your RAM chips\n");
...@@ -585,7 +586,8 @@ static void mem_parity_error(unsigned char reason, struct pt_regs * regs) ...@@ -585,7 +586,8 @@ static void mem_parity_error(unsigned char reason, struct pt_regs * regs)
outb(reason, 0x61); outb(reason, 0x61);
} }
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)
{ {
printk("NMI: IOCK error (debug interrupt?)\n"); printk("NMI: IOCK error (debug interrupt?)\n");
show_registers(regs); show_registers(regs);
...@@ -598,7 +600,8 @@ static void io_check_error(unsigned char reason, struct pt_regs * regs) ...@@ -598,7 +600,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)
{ printk("Uhhuh. NMI received for unknown reason %02x.\n", reason); { printk("Uhhuh. NMI received for unknown reason %02x.\n", reason);
printk("Dazed and confused, but trying to continue\n"); printk("Dazed and confused, but trying to continue\n");
printk("Do you have a strange power saving mode enabled?\n"); printk("Do you have a strange power saving mode enabled?\n");
...@@ -606,7 +609,7 @@ static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs) ...@@ -606,7 +609,7 @@ static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs)
/* Runs on IST stack. This code must keep interrupts off all the time. /* Runs on IST stack. This code must keep interrupts off all the time.
Nested NMIs are prevented by the CPU. */ Nested NMIs are prevented by the CPU. */
asmlinkage void default_do_nmi(struct pt_regs *regs) asmlinkage __kprobes void default_do_nmi(struct pt_regs *regs)
{ {
unsigned char reason = 0; unsigned char reason = 0;
int cpu; int cpu;
...@@ -658,7 +661,7 @@ asmlinkage void __kprobes do_int3(struct pt_regs * regs, long error_code) ...@@ -658,7 +661,7 @@ asmlinkage void __kprobes do_int3(struct pt_regs * regs, long error_code)
/* Help handler running on IST stack to switch back to user stack /* Help handler running on IST stack to switch back to user stack
for scheduling or signal handling. The actual stack switch is done in for scheduling or signal handling. The actual stack switch is done in
entry.S */ entry.S */
asmlinkage struct pt_regs *sync_regs(struct pt_regs *eregs) asmlinkage __kprobes struct pt_regs *sync_regs(struct pt_regs *eregs)
{ {
struct pt_regs *regs = eregs; struct pt_regs *regs = eregs;
/* Did already sync */ /* Did already sync */
......
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