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

Commit 377fad3a authored by Jeff Dike's avatar Jeff Dike Committed by Linus Torvalds
Browse files

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: default avatarJeff Dike <jdike@linux.intel.com>
Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 5d86456d
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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));
+2 −0
Original line number Diff line number Diff line
@@ -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
+7 −3
Original line number Diff line number Diff line
@@ -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)
	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;
+13 −2
Original line number Diff line number Diff line
@@ -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 ||
+24 −17
Original line number Diff line number Diff line
@@ -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,22 +52,7 @@ 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(&regs->regs, &sc);

	err = restore_fp_registers(userspace_pid[0], fpregs);
	if(err < 0) {
Loading