summaryrefslogtreecommitdiffstats
path: root/arch/um/kernel
diff options
context:
space:
mode:
authorRichard Weinberger <richard@nod.at>2013-09-23 17:38:02 +0200
committerRichard Weinberger <richard@nod.at>2013-11-17 11:27:30 +0100
commitf72c22e45e8f8fe78c7f793d983bee5bed63497e (patch)
tree0e3051e2d357548d086a2ac00c4bfe201fe4e31d /arch/um/kernel
parent9d1ee8ce92e16c6aa0a3fd91ee8ed9e403b3a2eb (diff)
downloadlinux-stable-f72c22e45e8f8fe78c7f793d983bee5bed63497e.tar.gz
linux-stable-f72c22e45e8f8fe78c7f793d983bee5bed63497e.tar.bz2
linux-stable-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')
-rw-r--r--arch/um/kernel/sysrq.c32
-rw-r--r--arch/um/kernel/trap.c14
2 files changed, 34 insertions, 12 deletions
diff --git a/arch/um/kernel/sysrq.c b/arch/um/kernel/sysrq.c
index 33cc72e26c6e..7122bf9c753e 100644
--- a/arch/um/kernel/sysrq.c
+++ b/arch/um/kernel/sysrq.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/sched.h>
#include <asm/sysrq.h>
+#include <os.h>
struct stack_frame {
struct stack_frame *next_frame;
@@ -48,29 +49,42 @@ static void print_stack_trace(unsigned long *sp, unsigned long bp)
/*Stolen from arch/i386/kernel/traps.c */
static const int kstack_depth_to_print = 24;
-static unsigned long get_frame_pointer(struct task_struct *task)
+static unsigned long get_frame_pointer(struct task_struct *task,
+ struct pt_regs *segv_regs)
{
if (!task || task == current)
- return current_bp();
+ return segv_regs ? PT_REGS_BP(segv_regs) : current_bp();
else
return KSTK_EBP(task);
}
+static unsigned long *get_stack_pointer(struct task_struct *task,
+ struct pt_regs *segv_regs)
+{
+ if (!task || task == current)
+ return segv_regs ? (unsigned long *)PT_REGS_SP(segv_regs) : current_sp();
+ else
+ return (unsigned long *)KSTK_ESP(task);
+}
+
void show_stack(struct task_struct *task, unsigned long *stack)
{
unsigned long *sp = stack, bp = 0;
+ struct pt_regs *segv_regs = current->thread.segv_regs;
int i;
+ if (!segv_regs && os_is_signal_stack()) {
+ printk(KERN_ERR "Received SIGSEGV in SIGSEGV handler,"
+ " aborting stack trace!\n");
+ return;
+ }
+
#ifdef CONFIG_FRAME_POINTER
- bp = get_frame_pointer(task);
+ bp = get_frame_pointer(task, segv_regs);
#endif
- if (!stack) {
- if (!task || task == current)
- sp = current_sp();
- else
- sp = (unsigned long *)KSTK_ESP(task);
- }
+ if (!stack)
+ sp = get_stack_pointer(task, segv_regs);
printk(KERN_INFO "Stack:\n");
stack = sp;
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;
}