Pārlūkot izejas kodu

[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: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Andi Kleen 19 gadi atpakaļ
vecāks
revīzija
eddb6fb9a5
3 mainītis faili ar 18 papildinājumiem un 13 dzēšanām
  1. 2 1
      arch/x86_64/kernel/entry.S
  2. 4 3
      arch/x86_64/kernel/nmi.c
  3. 12 9
      arch/x86_64/kernel/traps.c

+ 2 - 1
arch/x86_64/kernel/entry.S

@@ -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:
 	cli
 	cli
 	jmp paranoid_userspace
 	jmp paranoid_userspace
 	CFI_ENDPROC
 	CFI_ENDPROC
+	.previous .text
 
 
 KPROBE_ENTRY(int3)
 KPROBE_ENTRY(int3)
  	INTR_FRAME
  	INTR_FRAME

+ 4 - 3
arch/x86_64/kernel/nmi.c

@@ -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)
  	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)
 	}
 	}
 }
 }
 
 
-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();
 
 

+ 12 - 9
arch/x86_64/kernel/traps.c

@@ -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)
 	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)
 		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)
 	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,
 	}
 	}
 }
 }
 
 
-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)
 	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)
 	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)
 
 
 /* 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)
 /* 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 */