Commit 06039754d775d3e48e4a292e4f353321205eff53

Authored by Fernando Luis Vázquez Cao
Committed by Andi Kleen
1 parent 6f6b1e0477

[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: Fernando Vazquez <fernando@intellilink.co.jp>
Signed-off-by: Andi Kleen <ak@suse.de>

Showing 3 changed files with 14 additions and 10 deletions Side-by-side Diff

arch/i386/kernel/entry.S
... ... @@ -729,7 +729,7 @@
729 729 * check whether we got an NMI on the debug path where the debug
730 730 * fault happened on the sysenter path.
731 731 */
732   -ENTRY(nmi)
  732 +KPROBE_ENTRY(nmi)
733 733 RING0_INT_FRAME
734 734 pushl %eax
735 735 CFI_ADJUST_CFA_OFFSET 4
... ... @@ -805,6 +805,7 @@
805 805 .align 4
806 806 .long 1b,iret_exc
807 807 .previous
  808 +KPROBE_END(nmi)
808 809  
809 810 KPROBE_ENTRY(int3)
810 811 RING0_INT_FRAME
arch/i386/kernel/nmi.c
... ... @@ -22,6 +22,7 @@
22 22 #include <linux/sysctl.h>
23 23 #include <linux/percpu.h>
24 24 #include <linux/dmi.h>
  25 +#include <linux/kprobes.h>
25 26  
26 27 #include <asm/smp.h>
27 28 #include <asm/nmi.h>
... ... @@ -882,7 +883,7 @@
882 883  
883 884 extern void die_nmi(struct pt_regs *, const char *msg);
884 885  
885   -int nmi_watchdog_tick (struct pt_regs * regs, unsigned reason)
  886 +__kprobes int nmi_watchdog_tick(struct pt_regs * regs, unsigned reason)
886 887 {
887 888  
888 889 /*
... ... @@ -962,8 +963,7 @@
962 963 * This matches the old behaviour.
963 964 */
964 965 rc = 1;
965   - } else
966   - printk(KERN_WARNING "Unknown enabled NMI hardware?!\n");
  966 + }
967 967 }
968 968 done:
969 969 return rc;
arch/i386/kernel/traps.c
... ... @@ -689,7 +689,8 @@
689 689 }
690 690 }
691 691  
692   -static void mem_parity_error(unsigned char reason, struct pt_regs * regs)
  692 +static __kprobes void
  693 +mem_parity_error(unsigned char reason, struct pt_regs * regs)
693 694 {
694 695 printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x on "
695 696 "CPU %d.\n", reason, smp_processor_id());
... ... @@ -704,7 +705,8 @@
704 705 clear_mem_error(reason);
705 706 }
706 707  
707   -static void io_check_error(unsigned char reason, struct pt_regs * regs)
  708 +static __kprobes void
  709 +io_check_error(unsigned char reason, struct pt_regs * regs)
708 710 {
709 711 unsigned long i;
710 712  
... ... @@ -720,7 +722,8 @@
720 722 outb(reason, 0x61);
721 723 }
722 724  
723   -static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs)
  725 +static __kprobes void
  726 +unknown_nmi_error(unsigned char reason, struct pt_regs * regs)
724 727 {
725 728 #ifdef CONFIG_MCA
726 729 /* Might actually be able to figure out what the guilty party
... ... @@ -741,7 +744,7 @@
741 744  
742 745 static DEFINE_SPINLOCK(nmi_print_lock);
743 746  
744   -void die_nmi (struct pt_regs *regs, const char *msg)
  747 +void __kprobes die_nmi(struct pt_regs *regs, const char *msg)
745 748 {
746 749 if (notify_die(DIE_NMIWATCHDOG, msg, regs, 0, 2, SIGINT) ==
747 750 NOTIFY_STOP)
... ... @@ -773,7 +776,7 @@
773 776 do_exit(SIGSEGV);
774 777 }
775 778  
776   -static void default_do_nmi(struct pt_regs * regs)
  779 +static __kprobes void default_do_nmi(struct pt_regs * regs)
777 780 {
778 781 unsigned char reason = 0;
779 782  
... ... @@ -811,7 +814,7 @@
811 814 reassert_nmi();
812 815 }
813 816  
814   -fastcall void do_nmi(struct pt_regs * regs, long error_code)
  817 +fastcall __kprobes void do_nmi(struct pt_regs * regs, long error_code)
815 818 {
816 819 int cpu;
817 820