summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/um/include/asm/processor-generic.h1
-rw-r--r--arch/um/include/shared/os.h1
-rw-r--r--arch/um/kernel/sysrq.c32
-rw-r--r--arch/um/kernel/trap.c14
-rw-r--r--arch/um/os-Linux/signal.c8
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;
struct thread_struct {
struct task_struct *saved_task;
struct pt_regs regs;
+ struct pt_regs *segv_regs;
int singlestep_syscall;
void *fault_addr;
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);
extern void unblock_signals(void);
extern int get_signals(void);
extern int set_signals(int enable);
+extern int os_is_signal_stack(void);
/* util.c */
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 @@
#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;
}
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)
return ret;
}
+
+int os_is_signal_stack(void)
+{
+ stack_t ss;
+ sigaltstack(NULL, &ss);
+
+ return ss.ss_flags & SS_ONSTACK;
+}