diff options
Diffstat (limited to 'arch/um')
-rw-r--r-- | arch/um/include/asm/processor-generic.h | 1 | ||||
-rw-r--r-- | arch/um/include/shared/os.h | 1 | ||||
-rw-r--r-- | arch/um/kernel/sysrq.c | 32 | ||||
-rw-r--r-- | arch/um/kernel/trap.c | 14 | ||||
-rw-r--r-- | arch/um/os-Linux/signal.c | 8 |
5 files changed, 44 insertions, 12 deletions
diff --git a/arch/um/include/asm/processor-generic.h b/arch/um/include/asm/processor-generic.h index c03cd5a02364..90469031297b 100644 --- a/arch/um/include/asm/processor-generic.h +++ b/arch/um/include/asm/processor-generic.h | |||
@@ -21,6 +21,7 @@ struct mm_struct; | |||
21 | struct thread_struct { | 21 | struct thread_struct { |
22 | struct task_struct *saved_task; | 22 | struct task_struct *saved_task; |
23 | struct pt_regs regs; | 23 | struct pt_regs regs; |
24 | struct pt_regs *segv_regs; | ||
24 | int singlestep_syscall; | 25 | int singlestep_syscall; |
25 | void *fault_addr; | 26 | void *fault_addr; |
26 | jmp_buf *fault_catcher; | 27 | jmp_buf *fault_catcher; |
diff --git a/arch/um/include/shared/os.h b/arch/um/include/shared/os.h index 021104d98cb3..75298d3358e7 100644 --- a/arch/um/include/shared/os.h +++ b/arch/um/include/shared/os.h | |||
@@ -227,6 +227,7 @@ extern void block_signals(void); | |||
227 | extern void unblock_signals(void); | 227 | extern void unblock_signals(void); |
228 | extern int get_signals(void); | 228 | extern int get_signals(void); |
229 | extern int set_signals(int enable); | 229 | extern int set_signals(int enable); |
230 | extern int os_is_signal_stack(void); | ||
230 | 231 | ||
231 | /* util.c */ | 232 | /* util.c */ |
232 | extern void stack_protections(unsigned long address); | 233 | extern void stack_protections(unsigned long address); |
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 | ||
16 | struct stack_frame { | 17 | struct 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 */ |
49 | static const int kstack_depth_to_print = 24; | 50 | static const int kstack_depth_to_print = 24; |
50 | 51 | ||
51 | static unsigned long get_frame_pointer(struct task_struct *task) | 52 | static 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 | ||
61 | static 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 | |||
59 | void show_stack(struct task_struct *task, unsigned long *stack) | 70 | void 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 | |||
269 | out: | ||
270 | if (regs) | ||
271 | current->thread.segv_regs = NULL; | ||
272 | |||
265 | return 0; | 273 | return 0; |
266 | } | 274 | } |
267 | 275 | ||
diff --git a/arch/um/os-Linux/signal.c b/arch/um/os-Linux/signal.c index 905924b773d3..7b605e4dfffa 100644 --- a/arch/um/os-Linux/signal.c +++ b/arch/um/os-Linux/signal.c | |||
@@ -304,3 +304,11 @@ int set_signals(int enable) | |||
304 | 304 | ||
305 | return ret; | 305 | return ret; |
306 | } | 306 | } |
307 | |||
308 | int os_is_signal_stack(void) | ||
309 | { | ||
310 | stack_t ss; | ||
311 | sigaltstack(NULL, &ss); | ||
312 | |||
313 | return ss.ss_flags & SS_ONSTACK; | ||
314 | } | ||