[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>
This commit is contained in:
Родитель
2f01942536
Коммит
eddb6fb9a5
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Загрузка…
Ссылка в новой задаче