Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: David S. Miller <davem@davemloft.net>

authored by

David Miller and committed by
David S. Miller
5b4fc388 caf539cd

+37 -10
+2 -1
arch/sparc/include/asm/switch_to_64.h
··· 67 67 } while(0) 68 68 69 69 void synchronize_user_stack(void); 70 - void fault_in_user_windows(void); 70 + struct pt_regs; 71 + void fault_in_user_windows(struct pt_regs *); 71 72 72 73 #endif /* __SPARC64_SWITCH_TO_64_H */
+19 -6
arch/sparc/kernel/process_64.c
··· 36 36 #include <linux/sysrq.h> 37 37 #include <linux/nmi.h> 38 38 #include <linux/context_tracking.h> 39 + #include <linux/signal.h> 39 40 40 41 #include <linux/uaccess.h> 41 42 #include <asm/page.h> ··· 522 521 force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *) sp, 0, current); 523 522 } 524 523 525 - void fault_in_user_windows(void) 524 + static const char uwfault32[] = KERN_INFO \ 525 + "%s[%d]: bad register window fault: SP %08lx (orig_sp %08lx) TPC %08lx O7 %08lx\n"; 526 + static const char uwfault64[] = KERN_INFO \ 527 + "%s[%d]: bad register window fault: SP %016lx (orig_sp %016lx) TPC %08lx O7 %016lx\n"; 528 + 529 + void fault_in_user_windows(struct pt_regs *regs) 526 530 { 527 531 struct thread_info *t = current_thread_info(); 528 532 unsigned long window; ··· 540 534 do { 541 535 struct reg_window *rwin = &t->reg_window[window]; 542 536 int winsize = sizeof(struct reg_window); 543 - unsigned long sp; 537 + unsigned long sp, orig_sp; 544 538 545 - sp = t->rwbuf_stkptrs[window]; 539 + orig_sp = sp = t->rwbuf_stkptrs[window]; 546 540 547 541 if (test_thread_64bit_stack(sp)) 548 542 sp += STACK_BIAS; ··· 553 547 stack_unaligned(sp); 554 548 555 549 if (unlikely(copy_to_user((char __user *)sp, 556 - rwin, winsize))) 550 + rwin, winsize))) { 551 + if (show_unhandled_signals) 552 + printk_ratelimited(is_compat_task() ? 553 + uwfault32 : uwfault64, 554 + current->comm, current->pid, 555 + sp, orig_sp, 556 + regs->tpc, 557 + regs->u_regs[UREG_I7]); 557 558 goto barf; 559 + } 558 560 } while (window--); 559 561 } 560 562 set_thread_wsaved(0); ··· 570 556 571 557 barf: 572 558 set_thread_wsaved(window + 1); 573 - user_exit(); 574 - do_exit(SIGILL); 559 + force_sig(SIGSEGV, current); 575 560 } 576 561 577 562 asmlinkage long sparc_do_fork(unsigned long clone_flags,
+1
arch/sparc/kernel/rtrap_64.S
··· 39 39 wrpr %g0, RTRAP_PSTATE_IRQOFF, %pstate 40 40 41 41 __handle_user_windows: 42 + add %sp, PTREGS_OFF, %o0 42 43 call fault_in_user_windows 43 44 661: wrpr %g0, RTRAP_PSTATE, %pstate 44 45 /* If userspace is using ADI, it could potentially pass
+10 -2
arch/sparc/kernel/signal32.c
··· 371 371 get_sigframe(ksig, regs, sigframe_size); 372 372 373 373 if (invalid_frame_pointer(sf, sigframe_size)) { 374 - do_exit(SIGILL); 374 + if (show_unhandled_signals) 375 + pr_info("%s[%d] bad frame in setup_frame32: %08lx TPC %08lx O7 %08lx\n", 376 + current->comm, current->pid, (unsigned long)sf, 377 + regs->tpc, regs->u_regs[UREG_I7]); 378 + force_sigsegv(ksig->sig, current); 375 379 return -EINVAL; 376 380 } 377 381 ··· 505 501 get_sigframe(ksig, regs, sigframe_size); 506 502 507 503 if (invalid_frame_pointer(sf, sigframe_size)) { 508 - do_exit(SIGILL); 504 + if (show_unhandled_signals) 505 + pr_info("%s[%d] bad frame in setup_rt_frame32: %08lx TPC %08lx O7 %08lx\n", 506 + current->comm, current->pid, (unsigned long)sf, 507 + regs->tpc, regs->u_regs[UREG_I7]); 508 + force_sigsegv(ksig->sig, current); 509 509 return -EINVAL; 510 510 } 511 511
+5 -1
arch/sparc/kernel/signal_64.c
··· 370 370 get_sigframe(ksig, regs, sf_size); 371 371 372 372 if (invalid_frame_pointer (sf)) { 373 - do_exit(SIGILL); /* won't return, actually */ 373 + if (show_unhandled_signals) 374 + pr_info("%s[%d] bad frame in setup_rt_frame: %016lx TPC %016lx O7 %016lx\n", 375 + current->comm, current->pid, (unsigned long)sf, 376 + regs->tpc, regs->u_regs[UREG_I7]); 377 + force_sigsegv(ksig->sig, current); 374 378 return -EINVAL; 375 379 } 376 380