Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d7f58d5f authored by David Miller's avatar David Miller Committed by Greg Kroah-Hartman
Browse files

sparc64: Make corrupted user stacks more debuggable.



[ Upstream commit 5b4fc3882a649c9411dd0dcad2ddb78e911d340e ]

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>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 9b5bc99b
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ do { save_and_clear_fpu(); \
} while(0)

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 */
+19 −6
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@
#include <linux/sysrq.h>
#include <linux/nmi.h>
#include <linux/context_tracking.h>
#include <linux/signal.h>

#include <linux/uaccess.h>
#include <asm/page.h>
@@ -521,7 +522,12 @@ static void stack_unaligned(unsigned long sp)
	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();
	unsigned long window;
@@ -534,9 +540,9 @@ void fault_in_user_windows(void)
		do {
			struct reg_window *rwin = &t->reg_window[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))
				sp += STACK_BIAS;
@@ -547,8 +553,16 @@ void fault_in_user_windows(void)
				stack_unaligned(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;
			}
		} while (window--);
	}
	set_thread_wsaved(0);
@@ -556,8 +570,7 @@ void fault_in_user_windows(void)

barf:
	set_thread_wsaved(window + 1);
	user_exit();
	do_exit(SIGILL);
	force_sig(SIGSEGV, current);
}

asmlinkage long sparc_do_fork(unsigned long clone_flags,
+1 −0
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ __handle_preemption:
		 wrpr			%g0, RTRAP_PSTATE_IRQOFF, %pstate

__handle_user_windows:
		add			%sp, PTREGS_OFF, %o0
		call			fault_in_user_windows
661:		 wrpr			%g0, RTRAP_PSTATE, %pstate
		/* If userspace is using ADI, it could potentially pass
+10 −2
Original line number Diff line number Diff line
@@ -371,7 +371,11 @@ static int setup_frame32(struct ksignal *ksig, struct pt_regs *regs,
		get_sigframe(ksig, regs, 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;
	}

@@ -501,7 +505,11 @@ static int setup_rt_frame32(struct ksignal *ksig, struct pt_regs *regs,
		get_sigframe(ksig, regs, 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;
	}

+5 −1
Original line number Diff line number Diff line
@@ -370,7 +370,11 @@ setup_rt_frame(struct ksignal *ksig, struct pt_regs *regs)
		get_sigframe(ksig, regs, sf_size);

	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;
	}