diff options
author | Richard Weinberger <richard@nod.at> | 2013-09-23 17:38:02 +0200 |
---|---|---|
committer | Richard Weinberger <richard@nod.at> | 2013-11-17 11:27:30 +0100 |
commit | f72c22e45e8f8fe78c7f793d983bee5bed63497e (patch) | |
tree | 0e3051e2d357548d086a2ac00c4bfe201fe4e31d /arch/um/kernel/trap.c | |
parent | 9d1ee8ce92e16c6aa0a3fd91ee8ed9e403b3a2eb (diff) | |
download | linux-f72c22e45e8f8fe78c7f793d983bee5bed63497e.tar.gz linux-f72c22e45e8f8fe78c7f793d983bee5bed63497e.tar.bz2 linux-f72c22e45e8f8fe78c7f793d983bee5bed63497e.zip |
um: Make stack trace reliable against kernel mode faults
As UML uses an alternative signal stack we cannot use
the current stack pointer for stack dumping if UML itself
dies by SIGSEGV. To bypass this issue we save regs taken
from mcontext in our segv handler into thread_struct and
use these regs to obtain the stack pointer in show_stack().
Signed-off-by: Richard Weinberger <richard@nod.at>
Diffstat (limited to 'arch/um/kernel/trap.c')
-rw-r--r-- | arch/um/kernel/trap.c | 14 |
1 files changed, 11 insertions, 3 deletions
diff --git a/arch/um/kernel/trap.c b/arch/um/kernel/trap.c index 5c3aef74237f..974b87474a99 100644 --- a/arch/um/kernel/trap.c +++ b/arch/um/kernel/trap.c @@ -206,9 +206,12 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, int is_write = FAULT_WRITE(fi); unsigned long address = FAULT_ADDRESS(fi); + if (regs) + current->thread.segv_regs = container_of(regs, struct pt_regs, regs); + if (!is_user && (address >= start_vm) && (address < end_vm)) { flush_tlb_kernel_vm(); - return 0; + goto out; } else if (current->mm == NULL) { show_regs(container_of(regs, struct pt_regs, regs)); @@ -230,7 +233,7 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, catcher = current->thread.fault_catcher; if (!err) - return 0; + goto out; else if (catcher != NULL) { current->thread.fault_addr = (void *) address; UML_LONGJMP(catcher, 1); @@ -238,7 +241,7 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, else if (current->thread.fault_addr != NULL) panic("fault_addr set but no fault catcher"); else if (!is_user && arch_fixup(ip, regs)) - return 0; + goto out; if (!is_user) { show_regs(container_of(regs, struct pt_regs, regs)); @@ -262,6 +265,11 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, current->thread.arch.faultinfo = fi; force_sig_info(SIGSEGV, &si, current); } + +out: + if (regs) + current->thread.segv_regs = NULL; + return 0; } |