diff options
author | Steven Rostedt <srostedt@redhat.com> | 2011-12-09 03:02:19 -0500 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2011-12-21 15:38:55 -0500 |
commit | 228bdaa95fb830e08b6acd1afd4d2c55093cabfa (patch) | |
tree | 11d91c3d9f5b576003a07852fcf31eb2ec53bc39 /arch/x86/include/asm | |
parent | 3f3c8b8c4b2a34776c3470142a7c8baafcda6eb0 (diff) |
x86: Keep current stack in NMI breakpoints
We want to allow NMI handlers to have breakpoints to be able to
remove stop_machine from ftrace, kprobes and jump_labels. But if
an NMI interrupts a current breakpoint, and then it triggers a
breakpoint itself, it will switch to the breakpoint stack and
corrupt the data on it for the breakpoint processing that it
interrupted.
Instead, have the NMI check if it interrupted breakpoint processing
by checking if the stack that is currently used is a breakpoint
stack. If it is, then load a special IDT that changes the IST
for the debug exception to keep the same stack in kernel context.
When the NMI is done, it puts it back.
This way, if the NMI does trigger a breakpoint, it will keep
using the same stack and not stomp on the breakpoint data for
the breakpoint it interrupted.
Suggested-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'arch/x86/include/asm')
-rw-r--r-- | arch/x86/include/asm/desc.h | 12 | ||||
-rw-r--r-- | arch/x86/include/asm/processor.h | 6 |
2 files changed, 18 insertions, 0 deletions
diff --git a/arch/x86/include/asm/desc.h b/arch/x86/include/asm/desc.h index 41935fadfdfc..e95822d683f4 100644 --- a/arch/x86/include/asm/desc.h +++ b/arch/x86/include/asm/desc.h | |||
@@ -35,6 +35,8 @@ static inline void fill_ldt(struct desc_struct *desc, const struct user_desc *in | |||
35 | 35 | ||
36 | extern struct desc_ptr idt_descr; | 36 | extern struct desc_ptr idt_descr; |
37 | extern gate_desc idt_table[]; | 37 | extern gate_desc idt_table[]; |
38 | extern struct desc_ptr nmi_idt_descr; | ||
39 | extern gate_desc nmi_idt_table[]; | ||
38 | 40 | ||
39 | struct gdt_page { | 41 | struct gdt_page { |
40 | struct desc_struct gdt[GDT_ENTRIES]; | 42 | struct desc_struct gdt[GDT_ENTRIES]; |
@@ -307,6 +309,16 @@ static inline void set_desc_limit(struct desc_struct *desc, unsigned long limit) | |||
307 | desc->limit = (limit >> 16) & 0xf; | 309 | desc->limit = (limit >> 16) & 0xf; |
308 | } | 310 | } |
309 | 311 | ||
312 | #ifdef CONFIG_X86_64 | ||
313 | static inline void set_nmi_gate(int gate, void *addr) | ||
314 | { | ||
315 | gate_desc s; | ||
316 | |||
317 | pack_gate(&s, GATE_INTERRUPT, (unsigned long)addr, 0, 0, __KERNEL_CS); | ||
318 | write_idt_entry(nmi_idt_table, gate, &s); | ||
319 | } | ||
320 | #endif | ||
321 | |||
310 | static inline void _set_gate(int gate, unsigned type, void *addr, | 322 | static inline void _set_gate(int gate, unsigned type, void *addr, |
311 | unsigned dpl, unsigned ist, unsigned seg) | 323 | unsigned dpl, unsigned ist, unsigned seg) |
312 | { | 324 | { |
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index b650435ffb53..4b39d6d7e3a1 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h | |||
@@ -402,6 +402,9 @@ DECLARE_PER_CPU(char *, irq_stack_ptr); | |||
402 | DECLARE_PER_CPU(unsigned int, irq_count); | 402 | DECLARE_PER_CPU(unsigned int, irq_count); |
403 | extern unsigned long kernel_eflags; | 403 | extern unsigned long kernel_eflags; |
404 | extern asmlinkage void ignore_sysret(void); | 404 | extern asmlinkage void ignore_sysret(void); |
405 | int is_debug_stack(unsigned long addr); | ||
406 | void debug_stack_set_zero(void); | ||
407 | void debug_stack_reset(void); | ||
405 | #else /* X86_64 */ | 408 | #else /* X86_64 */ |
406 | #ifdef CONFIG_CC_STACKPROTECTOR | 409 | #ifdef CONFIG_CC_STACKPROTECTOR |
407 | /* | 410 | /* |
@@ -416,6 +419,9 @@ struct stack_canary { | |||
416 | }; | 419 | }; |
417 | DECLARE_PER_CPU_ALIGNED(struct stack_canary, stack_canary); | 420 | DECLARE_PER_CPU_ALIGNED(struct stack_canary, stack_canary); |
418 | #endif | 421 | #endif |
422 | static inline int is_debug_stack(unsigned long addr) { return 0; } | ||
423 | static inline void debug_stack_set_zero(void) { } | ||
424 | static inline void debug_stack_reset(void) { } | ||
419 | #endif /* X86_64 */ | 425 | #endif /* X86_64 */ |
420 | 426 | ||
421 | extern unsigned int xstate_size; | 427 | extern unsigned int xstate_size; |