| From: Steven Rostedt <rostedt@goodmis.org> |
| Subject: x86: Do not disable preemption in int3 on 32bit |
| |
| Preemption must be disabled before enabling interrupts in do_trap |
| on x86_64 because the stack in use for int3 and debug is a per CPU |
| stack set by th IST. But 32bit does not have an IST and the stack |
| still belongs to the current task and there is no problem in scheduling |
| out the task. |
| |
| Keep preemption enabled on X86_32 when enabling interrupts for |
| do_trap(). |
| |
| The name of the function is changed from preempt_conditional_sti/cli() |
| to conditional_sti/cli_ist(), to annotate that this function is used |
| when the stack is on the IST. |
| |
| Cc: stable-rt@vger.kernel.org |
| Signed-off-by: Steven Rostedt <rostedt@goodmis.org> |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| |
| --- |
| arch/x86/kernel/traps.c | 32 +++++++++++++++++++++++--------- |
| 1 file changed, 23 insertions(+), 9 deletions(-) |
| |
| Index: linux-stable/arch/x86/kernel/traps.c |
| =================================================================== |
| --- linux-stable.orig/arch/x86/kernel/traps.c |
| +++ linux-stable/arch/x86/kernel/traps.c |
| @@ -87,9 +87,21 @@ static inline void conditional_sti(struc |
| local_irq_enable(); |
| } |
| |
| -static inline void preempt_conditional_sti(struct pt_regs *regs) |
| +static inline void conditional_sti_ist(struct pt_regs *regs) |
| { |
| +#ifdef CONFIG_X86_64 |
| + /* |
| + * X86_64 uses a per CPU stack on the IST for certain traps |
| + * like int3. The task can not be preempted when using one |
| + * of these stacks, thus preemption must be disabled, otherwise |
| + * the stack can be corrupted if the task is scheduled out, |
| + * and another task comes in and uses this stack. |
| + * |
| + * On x86_32 the task keeps its own stack and it is OK if the |
| + * task schedules out. |
| + */ |
| inc_preempt_count(); |
| +#endif |
| if (regs->flags & X86_EFLAGS_IF) |
| local_irq_enable(); |
| } |
| @@ -100,11 +112,13 @@ static inline void conditional_cli(struc |
| local_irq_disable(); |
| } |
| |
| -static inline void preempt_conditional_cli(struct pt_regs *regs) |
| +static inline void conditional_cli_ist(struct pt_regs *regs) |
| { |
| if (regs->flags & X86_EFLAGS_IF) |
| local_irq_disable(); |
| +#ifdef CONFIG_X86_64 |
| dec_preempt_count(); |
| +#endif |
| } |
| |
| static void __kprobes |
| @@ -225,9 +239,9 @@ dotraplinkage void do_stack_segment(stru |
| if (notify_die(DIE_TRAP, "stack segment", regs, error_code, |
| X86_TRAP_SS, SIGBUS) == NOTIFY_STOP) |
| return; |
| - preempt_conditional_sti(regs); |
| + conditional_sti_ist(regs); |
| do_trap(X86_TRAP_SS, SIGBUS, "stack segment", regs, error_code, NULL); |
| - preempt_conditional_cli(regs); |
| + conditional_cli_ist(regs); |
| } |
| |
| dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code) |
| @@ -327,9 +341,9 @@ dotraplinkage void __kprobes notrace do_ |
| * as we may switch to the interrupt stack. |
| */ |
| debug_stack_usage_inc(); |
| - preempt_conditional_sti(regs); |
| + conditional_sti_ist(regs); |
| do_trap(X86_TRAP_BP, SIGTRAP, "int3", regs, error_code, NULL); |
| - preempt_conditional_cli(regs); |
| + conditional_cli_ist(regs); |
| debug_stack_usage_dec(); |
| } |
| |
| @@ -430,12 +444,12 @@ dotraplinkage void __kprobes do_debug(st |
| debug_stack_usage_inc(); |
| |
| /* It's safe to allow irq's after DR6 has been saved */ |
| - preempt_conditional_sti(regs); |
| + conditional_sti_ist(regs); |
| |
| if (regs->flags & X86_VM_MASK) { |
| handle_vm86_trap((struct kernel_vm86_regs *) regs, error_code, |
| X86_TRAP_DB); |
| - preempt_conditional_cli(regs); |
| + conditional_cli_ist(regs); |
| debug_stack_usage_dec(); |
| return; |
| } |
| @@ -455,7 +469,7 @@ dotraplinkage void __kprobes do_debug(st |
| si_code = get_si_code(tsk->thread.debugreg6); |
| if (tsk->thread.debugreg6 & (DR_STEP | DR_TRAP_BITS) || user_icebp) |
| send_sigtrap(tsk, regs, error_code, si_code); |
| - preempt_conditional_cli(regs); |
| + conditional_cli_ist(regs); |
| debug_stack_usage_dec(); |
| |
| return; |