Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Andy Lutomirski and committed by
Ingo Molnar
2deb4be2 46aea387

+31 -1
+11
arch/x86/entry/entry_32.S
··· 1153 1153 jmp error_code 1154 1154 END(async_page_fault) 1155 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)
+11
arch/x86/entry/entry_64.S
··· 1423 1423 mov $-ENOSYS, %eax 1424 1424 sysret 1425 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)
+9 -1
arch/x86/kernel/dumpstack.c
··· 234 234 EXPORT_SYMBOL_GPL(oops_begin); 235 235 NOKPROBE_SYMBOL(oops_begin); 236 236 237 + void __noreturn rewind_stack_do_exit(int signr); 238 + 237 239 void oops_end(unsigned long flags, struct pt_regs *regs, int signr) 238 240 { 239 241 if (regs && kexec_should_crash(current)) ··· 257 255 panic("Fatal exception in interrupt"); 258 256 if (panic_on_oops) 259 257 panic("Fatal exception"); 260 - do_exit(signr); 258 + 259 + /* 260 + * We're not going to return, but we might be on an IST stack or 261 + * have very little stack space left. Rewind the stack and kill 262 + * the task. 263 + */ 264 + rewind_stack_do_exit(signr); 261 265 } 262 266 NOKPROBE_SYMBOL(oops_end); 263 267