aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2011-12-09 03:02:19 -0500
committerSteven Rostedt <rostedt@goodmis.org>2011-12-21 15:38:55 -0500
commit228bdaa95fb830e08b6acd1afd4d2c55093cabfa (patch)
tree11d91c3d9f5b576003a07852fcf31eb2ec53bc39 /arch
parent3f3c8b8c4b2a34776c3470142a7c8baafcda6eb0 (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')
-rw-r--r--arch/x86/include/asm/desc.h12
-rw-r--r--arch/x86/include/asm/processor.h6
-rw-r--r--arch/x86/kernel/cpu/common.c22
-rw-r--r--arch/x86/kernel/head_64.S4
-rw-r--r--arch/x86/kernel/nmi.c15
-rw-r--r--arch/x86/kernel/traps.c6
6 files changed, 65 insertions, 0 deletions
diff --git a/arch/x86/include/asm/desc.h b/arch/x86/include/asm/desc.h
index 41935fadfdf..e95822d683f 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
36extern struct desc_ptr idt_descr; 36extern struct desc_ptr idt_descr;
37extern gate_desc idt_table[]; 37extern gate_desc idt_table[];
38extern struct desc_ptr nmi_idt_descr;
39extern gate_desc nmi_idt_table[];
38 40
39struct gdt_page { 41struct 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
313static 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
310static inline void _set_gate(int gate, unsigned type, void *addr, 322static 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 b650435ffb5..4b39d6d7e3a 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);
402DECLARE_PER_CPU(unsigned int, irq_count); 402DECLARE_PER_CPU(unsigned int, irq_count);
403extern unsigned long kernel_eflags; 403extern unsigned long kernel_eflags;
404extern asmlinkage void ignore_sysret(void); 404extern asmlinkage void ignore_sysret(void);
405int is_debug_stack(unsigned long addr);
406void debug_stack_set_zero(void);
407void 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};
417DECLARE_PER_CPU_ALIGNED(struct stack_canary, stack_canary); 420DECLARE_PER_CPU_ALIGNED(struct stack_canary, stack_canary);
418#endif 421#endif
422static inline int is_debug_stack(unsigned long addr) { return 0; }
423static inline void debug_stack_set_zero(void) { }
424static inline void debug_stack_reset(void) { }
419#endif /* X86_64 */ 425#endif /* X86_64 */
420 426
421extern unsigned int xstate_size; 427extern unsigned int xstate_size;
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index aa003b13a83..caa404556b9 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -1026,6 +1026,8 @@ __setup("clearcpuid=", setup_disablecpuid);
1026 1026
1027#ifdef CONFIG_X86_64 1027#ifdef CONFIG_X86_64
1028struct desc_ptr idt_descr = { NR_VECTORS * 16 - 1, (unsigned long) idt_table }; 1028struct desc_ptr idt_descr = { NR_VECTORS * 16 - 1, (unsigned long) idt_table };
1029struct desc_ptr nmi_idt_descr = { NR_VECTORS * 16 - 1,
1030 (unsigned long) nmi_idt_table };
1029 1031
1030DEFINE_PER_CPU_FIRST(union irq_stack_union, 1032DEFINE_PER_CPU_FIRST(union irq_stack_union,
1031 irq_stack_union) __aligned(PAGE_SIZE); 1033 irq_stack_union) __aligned(PAGE_SIZE);
@@ -1090,6 +1092,24 @@ unsigned long kernel_eflags;
1090 */ 1092 */
1091DEFINE_PER_CPU(struct orig_ist, orig_ist); 1093DEFINE_PER_CPU(struct orig_ist, orig_ist);
1092 1094
1095static DEFINE_PER_CPU(unsigned long, debug_stack_addr);
1096
1097int is_debug_stack(unsigned long addr)
1098{
1099 return addr <= __get_cpu_var(debug_stack_addr) &&
1100 addr > (__get_cpu_var(debug_stack_addr) - DEBUG_STKSZ);
1101}
1102
1103void debug_stack_set_zero(void)
1104{
1105 load_idt((const struct desc_ptr *)&nmi_idt_descr);
1106}
1107
1108void debug_stack_reset(void)
1109{
1110 load_idt((const struct desc_ptr *)&idt_descr);
1111}
1112
1093#else /* CONFIG_X86_64 */ 1113#else /* CONFIG_X86_64 */
1094 1114
1095DEFINE_PER_CPU(struct task_struct *, current_task) = &init_task; 1115DEFINE_PER_CPU(struct task_struct *, current_task) = &init_task;
@@ -1208,6 +1228,8 @@ void __cpuinit cpu_init(void)
1208 estacks += exception_stack_sizes[v]; 1228 estacks += exception_stack_sizes[v];
1209 oist->ist[v] = t->x86_tss.ist[v] = 1229 oist->ist[v] = t->x86_tss.ist[v] =
1210 (unsigned long)estacks; 1230 (unsigned long)estacks;
1231 if (v == DEBUG_STACK-1)
1232 per_cpu(debug_stack_addr, cpu) = (unsigned long)estacks;
1211 } 1233 }
1212 } 1234 }
1213 1235
diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S
index e11e39478a4..40f4eb3766d 100644
--- a/arch/x86/kernel/head_64.S
+++ b/arch/x86/kernel/head_64.S
@@ -417,6 +417,10 @@ ENTRY(phys_base)
417ENTRY(idt_table) 417ENTRY(idt_table)
418 .skip IDT_ENTRIES * 16 418 .skip IDT_ENTRIES * 16
419 419
420 .align L1_CACHE_BYTES
421ENTRY(nmi_idt_table)
422 .skip IDT_ENTRIES * 16
423
420 __PAGE_ALIGNED_BSS 424 __PAGE_ALIGNED_BSS
421 .align PAGE_SIZE 425 .align PAGE_SIZE
422ENTRY(empty_zero_page) 426ENTRY(empty_zero_page)
diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c
index e88f37b58dd..de8d4b333f4 100644
--- a/arch/x86/kernel/nmi.c
+++ b/arch/x86/kernel/nmi.c
@@ -408,6 +408,18 @@ static notrace __kprobes void default_do_nmi(struct pt_regs *regs)
408dotraplinkage notrace __kprobes void 408dotraplinkage notrace __kprobes void
409do_nmi(struct pt_regs *regs, long error_code) 409do_nmi(struct pt_regs *regs, long error_code)
410{ 410{
411 int update_debug_stack = 0;
412
413 /*
414 * If we interrupted a breakpoint, it is possible that
415 * the nmi handler will have breakpoints too. We need to
416 * change the IDT such that breakpoints that happen here
417 * continue to use the NMI stack.
418 */
419 if (unlikely(is_debug_stack(regs->sp))) {
420 debug_stack_set_zero();
421 update_debug_stack = 1;
422 }
411 nmi_enter(); 423 nmi_enter();
412 424
413 inc_irq_stat(__nmi_count); 425 inc_irq_stat(__nmi_count);
@@ -416,6 +428,9 @@ do_nmi(struct pt_regs *regs, long error_code)
416 default_do_nmi(regs); 428 default_do_nmi(regs);
417 429
418 nmi_exit(); 430 nmi_exit();
431
432 if (unlikely(update_debug_stack))
433 debug_stack_reset();
419} 434}
420 435
421void stop_nmi(void) 436void stop_nmi(void)
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index a8e3eb83466..a93c5cabc36 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -723,4 +723,10 @@ void __init trap_init(void)
723 cpu_init(); 723 cpu_init();
724 724
725 x86_init.irqs.trap_init(); 725 x86_init.irqs.trap_init();
726
727#ifdef CONFIG_X86_64
728 memcpy(&nmi_idt_table, &idt_table, IDT_ENTRIES * 16);
729 set_nmi_gate(1, &debug);
730 set_nmi_gate(3, &int3);
731#endif
726} 732}