Commit 5b4fc388 authored by David Miller's avatar David Miller Committed by David S. Miller

sparc64: Make corrupted user stacks more debuggable.

Right now if we get a corrupted user stack frame we do a
do_exit(SIGILL) which is not helpful.

If under a debugger, this behavior causes the inferior process to
exit.  So the register and other state cannot be examined at the time
of the event.

Instead, conditionally log a rate limited kernel log message and then
force a SIGSEGV.

With bits and ideas borrowed (as usual) from powerpc.
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent caf539cd
...@@ -67,6 +67,7 @@ do { save_and_clear_fpu(); \ ...@@ -67,6 +67,7 @@ do { save_and_clear_fpu(); \
} while(0) } while(0)
void synchronize_user_stack(void); void synchronize_user_stack(void);
void fault_in_user_windows(void); struct pt_regs;
void fault_in_user_windows(struct pt_regs *);
#endif /* __SPARC64_SWITCH_TO_64_H */ #endif /* __SPARC64_SWITCH_TO_64_H */
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <linux/sysrq.h> #include <linux/sysrq.h>
#include <linux/nmi.h> #include <linux/nmi.h>
#include <linux/context_tracking.h> #include <linux/context_tracking.h>
#include <linux/signal.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <asm/page.h> #include <asm/page.h>
...@@ -521,7 +522,12 @@ static void stack_unaligned(unsigned long sp) ...@@ -521,7 +522,12 @@ static void stack_unaligned(unsigned long sp)
force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *) sp, 0, current); force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *) sp, 0, current);
} }
void fault_in_user_windows(void) static const char uwfault32[] = KERN_INFO \
"%s[%d]: bad register window fault: SP %08lx (orig_sp %08lx) TPC %08lx O7 %08lx\n";
static const char uwfault64[] = KERN_INFO \
"%s[%d]: bad register window fault: SP %016lx (orig_sp %016lx) TPC %08lx O7 %016lx\n";
void fault_in_user_windows(struct pt_regs *regs)
{ {
struct thread_info *t = current_thread_info(); struct thread_info *t = current_thread_info();
unsigned long window; unsigned long window;
...@@ -534,9 +540,9 @@ void fault_in_user_windows(void) ...@@ -534,9 +540,9 @@ void fault_in_user_windows(void)
do { do {
struct reg_window *rwin = &t->reg_window[window]; struct reg_window *rwin = &t->reg_window[window];
int winsize = sizeof(struct reg_window); int winsize = sizeof(struct reg_window);
unsigned long sp; unsigned long sp, orig_sp;
sp = t->rwbuf_stkptrs[window]; orig_sp = sp = t->rwbuf_stkptrs[window];
if (test_thread_64bit_stack(sp)) if (test_thread_64bit_stack(sp))
sp += STACK_BIAS; sp += STACK_BIAS;
...@@ -547,8 +553,16 @@ void fault_in_user_windows(void) ...@@ -547,8 +553,16 @@ void fault_in_user_windows(void)
stack_unaligned(sp); stack_unaligned(sp);
if (unlikely(copy_to_user((char __user *)sp, if (unlikely(copy_to_user((char __user *)sp,
rwin, winsize))) rwin, winsize))) {
if (show_unhandled_signals)
printk_ratelimited(is_compat_task() ?
uwfault32 : uwfault64,
current->comm, current->pid,
sp, orig_sp,
regs->tpc,
regs->u_regs[UREG_I7]);
goto barf; goto barf;
}
} while (window--); } while (window--);
} }
set_thread_wsaved(0); set_thread_wsaved(0);
...@@ -556,8 +570,7 @@ void fault_in_user_windows(void) ...@@ -556,8 +570,7 @@ void fault_in_user_windows(void)
barf: barf:
set_thread_wsaved(window + 1); set_thread_wsaved(window + 1);
user_exit(); force_sig(SIGSEGV, current);
do_exit(SIGILL);
} }
asmlinkage long sparc_do_fork(unsigned long clone_flags, asmlinkage long sparc_do_fork(unsigned long clone_flags,
......
...@@ -39,6 +39,7 @@ __handle_preemption: ...@@ -39,6 +39,7 @@ __handle_preemption:
wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate
__handle_user_windows: __handle_user_windows:
add %sp, PTREGS_OFF, %o0
call fault_in_user_windows call fault_in_user_windows
661: wrpr %g0, RTRAP_PSTATE, %pstate 661: wrpr %g0, RTRAP_PSTATE, %pstate
/* If userspace is using ADI, it could potentially pass /* If userspace is using ADI, it could potentially pass
......
...@@ -371,7 +371,11 @@ static int setup_frame32(struct ksignal *ksig, struct pt_regs *regs, ...@@ -371,7 +371,11 @@ static int setup_frame32(struct ksignal *ksig, struct pt_regs *regs,
get_sigframe(ksig, regs, sigframe_size); get_sigframe(ksig, regs, sigframe_size);
if (invalid_frame_pointer(sf, sigframe_size)) { if (invalid_frame_pointer(sf, sigframe_size)) {
do_exit(SIGILL); if (show_unhandled_signals)
pr_info("%s[%d] bad frame in setup_frame32: %08lx TPC %08lx O7 %08lx\n",
current->comm, current->pid, (unsigned long)sf,
regs->tpc, regs->u_regs[UREG_I7]);
force_sigsegv(ksig->sig, current);
return -EINVAL; return -EINVAL;
} }
...@@ -501,7 +505,11 @@ static int setup_rt_frame32(struct ksignal *ksig, struct pt_regs *regs, ...@@ -501,7 +505,11 @@ static int setup_rt_frame32(struct ksignal *ksig, struct pt_regs *regs,
get_sigframe(ksig, regs, sigframe_size); get_sigframe(ksig, regs, sigframe_size);
if (invalid_frame_pointer(sf, sigframe_size)) { if (invalid_frame_pointer(sf, sigframe_size)) {
do_exit(SIGILL); if (show_unhandled_signals)
pr_info("%s[%d] bad frame in setup_rt_frame32: %08lx TPC %08lx O7 %08lx\n",
current->comm, current->pid, (unsigned long)sf,
regs->tpc, regs->u_regs[UREG_I7]);
force_sigsegv(ksig->sig, current);
return -EINVAL; return -EINVAL;
} }
......
...@@ -370,7 +370,11 @@ setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs) ...@@ -370,7 +370,11 @@ setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs)
get_sigframe(ksig, regs, sf_size); get_sigframe(ksig, regs, sf_size);
if (invalid_frame_pointer (sf)) { if (invalid_frame_pointer (sf)) {
do_exit(SIGILL); /* won't return, actually */ if (show_unhandled_signals)
pr_info("%s[%d] bad frame in setup_rt_frame: %016lx TPC %016lx O7 %016lx\n",
current->comm, current->pid, (unsigned long)sf,
regs->tpc, regs->u_regs[UREG_I7]);
force_sigsegv(ksig->sig, current);
return -EINVAL; return -EINVAL;
} }
......
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