aboutsummaryrefslogtreecommitdiffstats
path: root/arch/um/kernel/sysrq.c
diff options
context:
space:
mode:
authorRichard Weinberger <richard@nod.at>2013-09-23 11:38:02 -0400
committerRichard Weinberger <richard@nod.at>2013-11-17 05:27:30 -0500
commitf72c22e45e8f8fe78c7f793d983bee5bed63497e (patch)
tree0e3051e2d357548d086a2ac00c4bfe201fe4e31d /arch/um/kernel/sysrq.c
parent9d1ee8ce92e16c6aa0a3fd91ee8ed9e403b3a2eb (diff)
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/sysrq.c')
-rw-r--r--arch/um/kernel/sysrq.c32
1 files changed, 23 insertions, 9 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 @@
12#include <linux/module.h> 12#include <linux/module.h>
13#include <linux/sched.h> 13#include <linux/sched.h>
14#include <asm/sysrq.h> 14#include <asm/sysrq.h>
15#include <os.h>
15 16
16struct stack_frame { 17struct stack_frame {
17 struct stack_frame *next_frame; 18 struct stack_frame *next_frame;
@@ -48,29 +49,42 @@ static void print_stack_trace(unsigned long *sp, unsigned long bp)
48/*Stolen from arch/i386/kernel/traps.c */ 49/*Stolen from arch/i386/kernel/traps.c */
49static const int kstack_depth_to_print = 24; 50static const int kstack_depth_to_print = 24;
50 51
51static unsigned long get_frame_pointer(struct task_struct *task) 52static unsigned long get_frame_pointer(struct task_struct *task,
53 struct pt_regs *segv_regs)
52{ 54{
53 if (!task || task == current) 55 if (!task || task == current)
54 return current_bp(); 56 return segv_regs ? PT_REGS_BP(segv_regs) : current_bp();
55 else 57 else
56 return KSTK_EBP(task); 58 return KSTK_EBP(task);
57} 59}
58 60
61static unsigned long *get_stack_pointer(struct task_struct *task,
62 struct pt_regs *segv_regs)
63{
64 if (!task || task == current)
65 return segv_regs ? (unsigned long *)PT_REGS_SP(segv_regs) : current_sp();
66 else
67 return (unsigned long *)KSTK_ESP(task);
68}
69
59void show_stack(struct task_struct *task, unsigned long *stack) 70void show_stack(struct task_struct *task, unsigned long *stack)
60{ 71{
61 unsigned long *sp = stack, bp = 0; 72 unsigned long *sp = stack, bp = 0;
73 struct pt_regs *segv_regs = current->thread.segv_regs;
62 int i; 74 int i;
63 75
76 if (!segv_regs && os_is_signal_stack()) {
77 printk(KERN_ERR "Received SIGSEGV in SIGSEGV handler,"
78 " aborting stack trace!\n");
79 return;
80 }
81
64#ifdef CONFIG_FRAME_POINTER 82#ifdef CONFIG_FRAME_POINTER
65 bp = get_frame_pointer(task); 83 bp = get_frame_pointer(task, segv_regs);
66#endif 84#endif
67 85
68 if (!stack) { 86 if (!stack)
69 if (!task || task == current) 87 sp = get_stack_pointer(task, segv_regs);
70 sp = current_sp();
71 else
72 sp = (unsigned long *)KSTK_ESP(task);
73 }
74 88
75 printk(KERN_INFO "Stack:\n"); 89 printk(KERN_INFO "Stack:\n");
76 stack = sp; 90 stack = sp;