diff options
author | Andy Lutomirski <luto@kernel.org> | 2016-07-14 16:22:55 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-07-15 04:26:28 -0400 |
commit | 2deb4be28077638591fe5fc593b7f8aabc140f42 (patch) | |
tree | 3c06fe6e99676de863c22112927b70c6fd99acc4 | |
parent | 46aea3873401836abb7f01200e7946e7d518b359 (diff) |
x86/dumpstack: When OOPSing, rewind the stack before do_exit()
If we call do_exit() with a clean stack, we greatly reduce the risk of
recursive oopses due to stack overflow in do_exit, and we allow
do_exit to work even if we OOPS from an IST stack. The latter gives
us a much better chance of surviving long enough after we detect a
stack overflow to write out our logs.
Signed-off-by: Andy Lutomirski <luto@kernel.org>
Reviewed-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/32f73ceb372ec61889598da5e5b145889b9f2e19.1468527351.git.luto@kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r-- | arch/x86/entry/entry_32.S | 11 | ||||
-rw-r--r-- | arch/x86/entry/entry_64.S | 11 | ||||
-rw-r--r-- | arch/x86/kernel/dumpstack.c | 10 |
3 files changed, 31 insertions, 1 deletions
diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S index 983e5d3a0d27..0b56666e6039 100644 --- a/arch/x86/entry/entry_32.S +++ b/arch/x86/entry/entry_32.S | |||
@@ -1153,3 +1153,14 @@ ENTRY(async_page_fault) | |||
1153 | jmp error_code | 1153 | jmp error_code |
1154 | END(async_page_fault) | 1154 | END(async_page_fault) |
1155 | #endif | 1155 | #endif |
1156 | |||
1157 | ENTRY(rewind_stack_do_exit) | ||
1158 | /* Prevent any naive code from trying to unwind to our caller. */ | ||
1159 | xorl %ebp, %ebp | ||
1160 | |||
1161 | movl PER_CPU_VAR(cpu_current_top_of_stack), %esi | ||
1162 | leal -TOP_OF_KERNEL_STACK_PADDING-PTREGS_SIZE(%esi), %esp | ||
1163 | |||
1164 | call do_exit | ||
1165 | 1: jmp 1b | ||
1166 | END(rewind_stack_do_exit) | ||
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S index 9ee0da1807ed..b846875aeea6 100644 --- a/arch/x86/entry/entry_64.S +++ b/arch/x86/entry/entry_64.S | |||
@@ -1423,3 +1423,14 @@ ENTRY(ignore_sysret) | |||
1423 | mov $-ENOSYS, %eax | 1423 | mov $-ENOSYS, %eax |
1424 | sysret | 1424 | sysret |
1425 | END(ignore_sysret) | 1425 | END(ignore_sysret) |
1426 | |||
1427 | ENTRY(rewind_stack_do_exit) | ||
1428 | /* Prevent any naive code from trying to unwind to our caller. */ | ||
1429 | xorl %ebp, %ebp | ||
1430 | |||
1431 | movq PER_CPU_VAR(cpu_current_top_of_stack), %rax | ||
1432 | leaq -TOP_OF_KERNEL_STACK_PADDING-PTREGS_SIZE(%rax), %rsp | ||
1433 | |||
1434 | call do_exit | ||
1435 | 1: jmp 1b | ||
1436 | END(rewind_stack_do_exit) | ||
diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c index cc88e25d73e9..de8242d8bb61 100644 --- a/arch/x86/kernel/dumpstack.c +++ b/arch/x86/kernel/dumpstack.c | |||
@@ -234,6 +234,8 @@ unsigned long oops_begin(void) | |||
234 | EXPORT_SYMBOL_GPL(oops_begin); | 234 | EXPORT_SYMBOL_GPL(oops_begin); |
235 | NOKPROBE_SYMBOL(oops_begin); | 235 | NOKPROBE_SYMBOL(oops_begin); |
236 | 236 | ||
237 | void __noreturn rewind_stack_do_exit(int signr); | ||
238 | |||
237 | void oops_end(unsigned long flags, struct pt_regs *regs, int signr) | 239 | void oops_end(unsigned long flags, struct pt_regs *regs, int signr) |
238 | { | 240 | { |
239 | if (regs && kexec_should_crash(current)) | 241 | if (regs && kexec_should_crash(current)) |
@@ -255,7 +257,13 @@ void oops_end(unsigned long flags, struct pt_regs *regs, int signr) | |||
255 | panic("Fatal exception in interrupt"); | 257 | panic("Fatal exception in interrupt"); |
256 | if (panic_on_oops) | 258 | if (panic_on_oops) |
257 | panic("Fatal exception"); | 259 | panic("Fatal exception"); |
258 | do_exit(signr); | 260 | |
261 | /* | ||
262 | * We're not going to return, but we might be on an IST stack or | ||
263 | * have very little stack space left. Rewind the stack and kill | ||
264 | * the task. | ||
265 | */ | ||
266 | rewind_stack_do_exit(signr); | ||
259 | } | 267 | } |
260 | NOKPROBE_SYMBOL(oops_end); | 268 | NOKPROBE_SYMBOL(oops_end); |
261 | 269 | ||