Commit f72c22e4 authored by Richard Weinberger's avatar Richard Weinberger

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: default avatarRichard Weinberger <richard@nod.at>
parent 9d1ee8ce
...@@ -21,6 +21,7 @@ struct mm_struct; ...@@ -21,6 +21,7 @@ struct mm_struct;
struct thread_struct { struct thread_struct {
struct task_struct *saved_task; struct task_struct *saved_task;
struct pt_regs regs; struct pt_regs regs;
struct pt_regs *segv_regs;
int singlestep_syscall; int singlestep_syscall;
void *fault_addr; void *fault_addr;
jmp_buf *fault_catcher; jmp_buf *fault_catcher;
......
...@@ -227,6 +227,7 @@ extern void block_signals(void); ...@@ -227,6 +227,7 @@ extern void block_signals(void);
extern void unblock_signals(void); extern void unblock_signals(void);
extern int get_signals(void); extern int get_signals(void);
extern int set_signals(int enable); extern int set_signals(int enable);
extern int os_is_signal_stack(void);
/* util.c */ /* util.c */
extern void stack_protections(unsigned long address); extern void stack_protections(unsigned long address);
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <asm/sysrq.h> #include <asm/sysrq.h>
#include <os.h>
struct stack_frame { struct stack_frame {
struct stack_frame *next_frame; struct stack_frame *next_frame;
...@@ -48,29 +49,42 @@ static void print_stack_trace(unsigned long *sp, unsigned long bp) ...@@ -48,29 +49,42 @@ static void print_stack_trace(unsigned long *sp, unsigned long bp)
/*Stolen from arch/i386/kernel/traps.c */ /*Stolen from arch/i386/kernel/traps.c */
static const int kstack_depth_to_print = 24; 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) if (!task || task == current)
return current_bp(); return segv_regs ? PT_REGS_BP(segv_regs) : current_bp();
else else
return KSTK_EBP(task); 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) void show_stack(struct task_struct *task, unsigned long *stack)
{ {
unsigned long *sp = stack, bp = 0; unsigned long *sp = stack, bp = 0;
struct pt_regs *segv_regs = current->thread.segv_regs;
int i; 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 #ifdef CONFIG_FRAME_POINTER
bp = get_frame_pointer(task); bp = get_frame_pointer(task, segv_regs);
#endif #endif
if (!stack) { if (!stack)
if (!task || task == current) sp = get_stack_pointer(task, segv_regs);
sp = current_sp();
else
sp = (unsigned long *)KSTK_ESP(task);
}
printk(KERN_INFO "Stack:\n"); printk(KERN_INFO "Stack:\n");
stack = sp; stack = sp;
......
...@@ -206,9 +206,12 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, ...@@ -206,9 +206,12 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
int is_write = FAULT_WRITE(fi); int is_write = FAULT_WRITE(fi);
unsigned long address = FAULT_ADDRESS(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)) { if (!is_user && (address >= start_vm) && (address < end_vm)) {
flush_tlb_kernel_vm(); flush_tlb_kernel_vm();
return 0; goto out;
} }
else if (current->mm == NULL) { else if (current->mm == NULL) {
show_regs(container_of(regs, struct pt_regs, regs)); 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,7 +233,7 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
catcher = current->thread.fault_catcher; catcher = current->thread.fault_catcher;
if (!err) if (!err)
return 0; goto out;
else if (catcher != NULL) { else if (catcher != NULL) {
current->thread.fault_addr = (void *) address; current->thread.fault_addr = (void *) address;
UML_LONGJMP(catcher, 1); UML_LONGJMP(catcher, 1);
...@@ -238,7 +241,7 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user, ...@@ -238,7 +241,7 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
else if (current->thread.fault_addr != NULL) else if (current->thread.fault_addr != NULL)
panic("fault_addr set but no fault catcher"); panic("fault_addr set but no fault catcher");
else if (!is_user && arch_fixup(ip, regs)) else if (!is_user && arch_fixup(ip, regs))
return 0; goto out;
if (!is_user) { if (!is_user) {
show_regs(container_of(regs, struct pt_regs, regs)); 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,6 +265,11 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
current->thread.arch.faultinfo = fi; current->thread.arch.faultinfo = fi;
force_sig_info(SIGSEGV, &si, current); force_sig_info(SIGSEGV, &si, current);
} }
out:
if (regs)
current->thread.segv_regs = NULL;
return 0; return 0;
} }
......
...@@ -304,3 +304,11 @@ int set_signals(int enable) ...@@ -304,3 +304,11 @@ int set_signals(int enable)
return ret; return ret;
} }
int os_is_signal_stack(void)
{
stack_t ss;
sigaltstack(NULL, &ss);
return ss.ss_flags & SS_ONSTACK;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment