aboutsummaryrefslogtreecommitdiffstats
path: root/arch/um/kernel
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
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')
-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 @@
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;
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,
206 int is_write = FAULT_WRITE(fi); 206 int is_write = FAULT_WRITE(fi);
207 unsigned long address = FAULT_ADDRESS(fi); 207 unsigned long address = FAULT_ADDRESS(fi);
208 208
209 if (regs)
210 current->thread.segv_regs = container_of(regs, struct pt_regs, regs);
211
209 if (!is_user && (address >= start_vm) && (address < end_vm)) { 212 if (!is_user && (address >= start_vm) && (address < end_vm)) {
210 flush_tlb_kernel_vm(); 213 flush_tlb_kernel_vm();
211 return 0; 214 goto out;
212 } 215 }
213 else if (current->mm == NULL) { 216 else if (current->mm == NULL) {
214 show_regs(container_of(regs, struct pt_regs, regs)); 217 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,
230 233
231 catcher = current->thread.fault_catcher; 234 catcher = current->thread.fault_catcher;
232 if (!err) 235 if (!err)
233 return 0; 236 goto out;
234 else if (catcher != NULL) { 237 else if (catcher != NULL) {
235 current->thread.fault_addr = (void *) address; 238 current->thread.fault_addr = (void *) address;
236 UML_LONGJMP(catcher, 1); 239 UML_LONGJMP(catcher, 1);
@@ -238,7 +241,7 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
238 else if (current->thread.fault_addr != NULL) 241 else if (current->thread.fault_addr != NULL)
239 panic("fault_addr set but no fault catcher"); 242 panic("fault_addr set but no fault catcher");
240 else if (!is_user && arch_fixup(ip, regs)) 243 else if (!is_user && arch_fixup(ip, regs))
241 return 0; 244 goto out;
242 245
243 if (!is_user) { 246 if (!is_user) {
244 show_regs(container_of(regs, struct pt_regs, regs)); 247 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,
262 current->thread.arch.faultinfo = fi; 265 current->thread.arch.faultinfo = fi;
263 force_sig_info(SIGSEGV, &si, current); 266 force_sig_info(SIGSEGV, &si, current);
264 } 267 }
268
269out:
270 if (regs)
271 current->thread.segv_regs = NULL;
272
265 return 0; 273 return 0;
266} 274}
267 275