uml: kernel segfaults should dump proper registers
If there's a segfault inside the kernel, we want a dump of the registers at the point of the segfault, not the registers at the point of calling panic or the last userspace registers. sig_handler_common_skas now uses a static register set in the case of a SIGSEGV to avoid messing up the process registers if the segfault turns out to be non-fatal. The architecture sigcontext-to-pt_regs copying code was repurposed to copy data out of the SEGV stack frame. Signed-off-by: Jeff Dike <jdike@linux.intel.com> Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
5d86456d38
commit
377fad3acb
6 changed files with 78 additions and 22 deletions
|
@ -24,5 +24,7 @@ DEFINE(UM_ELF_CLASS, ELF_CLASS);
|
|||
DEFINE(UM_ELFCLASS32, ELFCLASS32);
|
||||
DEFINE(UM_ELFCLASS64, ELFCLASS64);
|
||||
|
||||
DEFINE(UM_NR_CPUS, NR_CPUS);
|
||||
|
||||
/* For crypto assembler code. */
|
||||
DEFINE(crypto_tfm_ctx_offset, offsetof(struct crypto_tfm, __crt_ctx));
|
||||
|
|
|
@ -115,4 +115,6 @@ extern void time_init_kern(void);
|
|||
extern int __cant_sleep(void);
|
||||
extern void sigio_handler(int sig, union uml_pt_regs *regs);
|
||||
|
||||
extern void copy_sc(union uml_pt_regs *regs, void *from);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -170,8 +170,10 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
|
|||
flush_tlb_kernel_vm();
|
||||
return 0;
|
||||
}
|
||||
else if(current->mm == NULL)
|
||||
panic("Segfault with no mm");
|
||||
else if(current->mm == NULL) {
|
||||
show_regs(container_of(regs, struct pt_regs, regs));
|
||||
panic("Segfault with no mm");
|
||||
}
|
||||
|
||||
if (SEGV_IS_FIXABLE(&fi) || SEGV_MAYBE_FIXABLE(&fi))
|
||||
err = handle_page_fault(address, ip, is_write, is_user, &si.si_code);
|
||||
|
@ -194,9 +196,11 @@ unsigned long segv(struct faultinfo fi, unsigned long ip, int is_user,
|
|||
else if(!is_user && arch_fixup(ip, regs))
|
||||
return 0;
|
||||
|
||||
if(!is_user)
|
||||
if(!is_user) {
|
||||
show_regs(container_of(regs, struct pt_regs, regs));
|
||||
panic("Kernel mode fault at addr 0x%lx, ip 0x%lx",
|
||||
address, ip);
|
||||
}
|
||||
|
||||
if (err == -EACCES) {
|
||||
si.si_signo = SIGBUS;
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include "sysdep/ptrace_user.h"
|
||||
#include "os.h"
|
||||
|
||||
static union uml_pt_regs ksig_regs[UM_NR_CPUS];
|
||||
|
||||
void sig_handler_common_skas(int sig, void *sc_ptr)
|
||||
{
|
||||
struct sigcontext *sc = sc_ptr;
|
||||
|
@ -27,10 +29,19 @@ void sig_handler_common_skas(int sig, void *sc_ptr)
|
|||
* the process will die.
|
||||
* XXX Figure out why this is better than SA_NODEFER
|
||||
*/
|
||||
if(sig == SIGSEGV)
|
||||
if(sig == SIGSEGV) {
|
||||
change_sig(SIGSEGV, 1);
|
||||
/* For segfaults, we want the data from the
|
||||
* sigcontext. In this case, we don't want to mangle
|
||||
* the process registers, so use a static set of
|
||||
* registers. For other signals, the process
|
||||
* registers are OK.
|
||||
*/
|
||||
r = &ksig_regs[cpu()];
|
||||
copy_sc(r, sc_ptr);
|
||||
}
|
||||
else r = TASK_REGS(get_current());
|
||||
|
||||
r = TASK_REGS(get_current());
|
||||
save_user = r->skas.is_user;
|
||||
r->skas.is_user = 0;
|
||||
if ( sig == SIGFPE || sig == SIGSEGV ||
|
||||
|
|
|
@ -18,6 +18,28 @@
|
|||
|
||||
#include "skas.h"
|
||||
|
||||
void copy_sc(union uml_pt_regs *regs, void *from)
|
||||
{
|
||||
struct sigcontext *sc = from;
|
||||
|
||||
REGS_GS(regs->skas.regs) = sc->gs;
|
||||
REGS_FS(regs->skas.regs) = sc->fs;
|
||||
REGS_ES(regs->skas.regs) = sc->es;
|
||||
REGS_DS(regs->skas.regs) = sc->ds;
|
||||
REGS_EDI(regs->skas.regs) = sc->edi;
|
||||
REGS_ESI(regs->skas.regs) = sc->esi;
|
||||
REGS_EBP(regs->skas.regs) = sc->ebp;
|
||||
REGS_SP(regs->skas.regs) = sc->esp;
|
||||
REGS_EBX(regs->skas.regs) = sc->ebx;
|
||||
REGS_EDX(regs->skas.regs) = sc->edx;
|
||||
REGS_ECX(regs->skas.regs) = sc->ecx;
|
||||
REGS_EAX(regs->skas.regs) = sc->eax;
|
||||
REGS_IP(regs->skas.regs) = sc->eip;
|
||||
REGS_CS(regs->skas.regs) = sc->cs;
|
||||
REGS_EFLAGS(regs->skas.regs) = sc->eflags;
|
||||
REGS_SS(regs->skas.regs) = sc->ss;
|
||||
}
|
||||
|
||||
static int copy_sc_from_user_skas(struct pt_regs *regs,
|
||||
struct sigcontext __user *from)
|
||||
{
|
||||
|
@ -30,25 +52,10 @@ static int copy_sc_from_user_skas(struct pt_regs *regs,
|
|||
if(err)
|
||||
return err;
|
||||
|
||||
REGS_GS(regs->regs.skas.regs) = sc.gs;
|
||||
REGS_FS(regs->regs.skas.regs) = sc.fs;
|
||||
REGS_ES(regs->regs.skas.regs) = sc.es;
|
||||
REGS_DS(regs->regs.skas.regs) = sc.ds;
|
||||
REGS_EDI(regs->regs.skas.regs) = sc.edi;
|
||||
REGS_ESI(regs->regs.skas.regs) = sc.esi;
|
||||
REGS_EBP(regs->regs.skas.regs) = sc.ebp;
|
||||
REGS_SP(regs->regs.skas.regs) = sc.esp;
|
||||
REGS_EBX(regs->regs.skas.regs) = sc.ebx;
|
||||
REGS_EDX(regs->regs.skas.regs) = sc.edx;
|
||||
REGS_ECX(regs->regs.skas.regs) = sc.ecx;
|
||||
REGS_EAX(regs->regs.skas.regs) = sc.eax;
|
||||
REGS_IP(regs->regs.skas.regs) = sc.eip;
|
||||
REGS_CS(regs->regs.skas.regs) = sc.cs;
|
||||
REGS_EFLAGS(regs->regs.skas.regs) = sc.eflags;
|
||||
REGS_SS(regs->regs.skas.regs) = sc.ss;
|
||||
copy_sc(®s->regs, &sc);
|
||||
|
||||
err = restore_fp_registers(userspace_pid[0], fpregs);
|
||||
if(err < 0){
|
||||
if(err < 0) {
|
||||
printk("copy_sc_from_user_skas - PTRACE_SETFPREGS failed, "
|
||||
"errno = %d\n", -err);
|
||||
return err;
|
||||
|
|
|
@ -20,6 +20,36 @@
|
|||
|
||||
#include "skas.h"
|
||||
|
||||
void copy_sc(union uml_pt_regs *regs, void *from)
|
||||
{
|
||||
struct sigcontext *sc = from;
|
||||
|
||||
#define GETREG(regs, regno, sc, regname) \
|
||||
(regs)->skas.regs[(regno) / sizeof(unsigned long)] = (sc)->regname
|
||||
|
||||
GETREG(regs, R8, sc, r8);
|
||||
GETREG(regs, R9, sc, r9);
|
||||
GETREG(regs, R10, sc, r10);
|
||||
GETREG(regs, R11, sc, r11);
|
||||
GETREG(regs, R12, sc, r12);
|
||||
GETREG(regs, R13, sc, r13);
|
||||
GETREG(regs, R14, sc, r14);
|
||||
GETREG(regs, R15, sc, r15);
|
||||
GETREG(regs, RDI, sc, rdi);
|
||||
GETREG(regs, RSI, sc, rsi);
|
||||
GETREG(regs, RBP, sc, rbp);
|
||||
GETREG(regs, RBX, sc, rbx);
|
||||
GETREG(regs, RDX, sc, rdx);
|
||||
GETREG(regs, RAX, sc, rax);
|
||||
GETREG(regs, RCX, sc, rcx);
|
||||
GETREG(regs, RSP, sc, rsp);
|
||||
GETREG(regs, RIP, sc, rip);
|
||||
GETREG(regs, EFLAGS, sc, eflags);
|
||||
GETREG(regs, CS, sc, cs);
|
||||
|
||||
#undef GETREG
|
||||
}
|
||||
|
||||
static int copy_sc_from_user_skas(struct pt_regs *regs,
|
||||
struct sigcontext __user *from)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue