[PATCH 3.16 216/328] x86/process: Don't mix user/kernel regs in 64bit __show_regs()

Ben Hutchings
2018-12-09T21:59:15Z

From: Jann Horn <jannh@google.com>

commit 9fe6299dde587788f245e9f7a5a1b296fad4e8c7 upstream.

When the kernel.print-fatal-signals sysctl has been enabled, a simple
userspace crash will cause the kernel to write a crash dump that contains,
among other things, the kernel gsbase into dmesg.

As suggested by Andy, limit output to pt_regs, FS_BASE and KERNEL_GS_BASE
in this case.

This also moves the bitness-specific logic from show_regs() into
process_{32,64}.c.

Fixes: 45807a1df9f5 ("vdso: print fatal signals")
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bpetkov@suse.de>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Link: https://lkml.kernel.org/r/20180831194151.123586-1-jannh@google.com
[bwh: Backported to 3.16:
- Keep using user_mode_vm() to in 32-bit show_regs()
- Also update call to __show_regs() in kmemcheck
- Don't add redundant rdmsrl()s in 64-bit __show_regs()
- Adjust filenames, context]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
---
--- a/arch/x86/include/asm/kdebug.h
+++ b/arch/x86/include/asm/kdebug.h
@@ -21,12 +21,22 @@ enum die_val {
DIE_NMIUNKNOWN,
};

+enum show_regs_mode {
+ SHOW_REGS_SHORT,
+ /*
+ * For when userspace crashed, but we don't think it's our fault, and
+ * therefore don't print kernel registers.
+ */
+ SHOW_REGS_USER,
+ SHOW_REGS_ALL
+};
+
extern void printk_address(unsigned long address);
extern void die(const char *, struct pt_regs *,long);
extern int __must_check __die(const char *, struct pt_regs *, long);
extern void show_trace(struct task_struct *t, struct pt_regs *regs,
unsigned long *sp, unsigned long bp);
-extern void __show_regs(struct pt_regs *regs, int all);
+extern void __show_regs(struct pt_regs *regs, enum show_regs_mode);
extern unsigned long oops_begin(void);
extern void oops_end(unsigned long, struct pt_regs *, int signr);
#ifdef CONFIG_KEXEC
--- a/arch/x86/kernel/dumpstack_32.c
+++ b/arch/x86/kernel/dumpstack_32.c
@@ -123,7 +123,7 @@ void show_regs(struct pt_regs *regs)
int i;

show_regs_print_info(KERN_EMERG);
- __show_regs(regs, !user_mode_vm(regs));
+ __show_regs(regs, user_mode_vm(regs) ? SHOW_REGS_USER : SHOW_REGS_ALL);

/*
* When in-kernel, we also print out the stack and code at the
--- a/arch/x86/kernel/dumpstack_64.c
+++ b/arch/x86/kernel/dumpstack_64.c
@@ -301,7 +301,7 @@ void show_regs(struct pt_regs *regs)

sp = regs->sp;
show_regs_print_info(KERN_DEFAULT);
- __show_regs(regs, 1);
+ __show_regs(regs, user_mode(regs) ? SHOW_REGS_USER : SHOW_REGS_ALL);

/*
* When in-kernel, we also print out the stack and code at the
--- a/arch/x86/kernel/process_32.c
+++ b/arch/x86/kernel/process_32.c
@@ -66,7 +66,7 @@ unsigned long thread_saved_pc(struct tas
return ((unsigned long *)tsk->thread.sp)[3];
}

-void __show_regs(struct pt_regs *regs, int all)
+void __show_regs(struct pt_regs *regs, enum show_regs_mode mode)
{
unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L;
unsigned long d0, d1, d2, d3, d6, d7;
@@ -95,7 +95,7 @@ void __show_regs(struct pt_regs *regs, i
printk(KERN_DEFAULT " DS: %04x ES: %04x FS: %04x GS: %04x SS: %04x\n",
(u16)regs->ds, (u16)regs->es, (u16)regs->fs, gs, ss);

- if (!all)
+ if (mode != SHOW_REGS_ALL)
return;

cr0 = read_cr0();
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -56,7 +56,7 @@ asmlinkage extern void ret_from_fork(voi
__visible DEFINE_PER_CPU_USER_MAPPED(unsigned long, old_rsp);

/* Prints also some state that isn't saved in the pt_regs */
-void __show_regs(struct pt_regs *regs, int all)
+void __show_regs(struct pt_regs *regs, enum show_regs_mode mode)
{
unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L, fs, gs, shadowgs;
unsigned long d0, d1, d2, d3, d6, d7;
@@ -88,9 +88,15 @@ void __show_regs(struct pt_regs *regs, i
rdmsrl(MSR_GS_BASE, gs);
rdmsrl(MSR_KERNEL_GS_BASE, shadowgs);

- if (!all)
+ if (mode == SHOW_REGS_SHORT)
return;

+ if (mode == SHOW_REGS_USER) {
+ printk(KERN_DEFAULT "FS: %016lx GS: %016lx\n",
+ fs, shadowgs);
+ return;
+ }
+
cr0 = read_cr0();
cr2 = read_cr2();
cr3 = read_cr3();
--- a/arch/x86/mm/kmemcheck/error.c
+++ b/arch/x86/mm/kmemcheck/error.c
@@ -132,7 +132,7 @@ void kmemcheck_error_recall(void)
break;
}

- __show_regs(&e->regs, 1);
+ __show_regs(&e->regs, SHOW_REGS_ALL);
print_stack_trace(&e->trace, 0);
}