Skip to content

Commit 342db04

Browse files
thejhKAGA-KOKO
authored andcommitted
x86/dumpstack: Don't dump kernel memory based on usermode RIP
show_opcodes() is used both for dumping kernel instructions and for dumping user instructions. If userspace causes #PF by jumping to a kernel address, show_opcodes() can be reached with regs->ip controlled by the user, pointing to kernel code. Make sure that userspace can't trick us into dumping kernel memory into dmesg. Fixes: 7cccf07 ("x86/dumpstack: Add a show_ip() function") Signed-off-by: Jann Horn <jannh@google.com> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Kees Cook <keescook@chromium.org> Reviewed-by: Borislav Petkov <bp@suse.de> Cc: "H. Peter Anvin" <hpa@zytor.com> Cc: Andy Lutomirski <luto@kernel.org> Cc: security@kernel.org Cc: stable@vger.kernel.org Link: https://lkml.kernel.org/r/20180828154901.112726-1-jannh@google.com
1 parent 26e609e commit 342db04

File tree

3 files changed

+15
-5
lines changed

3 files changed

+15
-5
lines changed

arch/x86/include/asm/stacktrace.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,6 @@ static inline unsigned long caller_frame_pointer(void)
111111
return (unsigned long)frame;
112112
}
113113

114-
void show_opcodes(u8 *rip, const char *loglvl);
114+
void show_opcodes(struct pt_regs *regs, const char *loglvl);
115115
void show_ip(struct pt_regs *regs, const char *loglvl);
116116
#endif /* _ASM_X86_STACKTRACE_H */

arch/x86/kernel/dumpstack.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,24 @@ static void printk_stack_address(unsigned long address, int reliable,
9090
* Thus, the 2/3rds prologue and 64 byte OPCODE_BUFSIZE is just a random
9191
* guesstimate in attempt to achieve all of the above.
9292
*/
93-
void show_opcodes(u8 *rip, const char *loglvl)
93+
void show_opcodes(struct pt_regs *regs, const char *loglvl)
9494
{
9595
#define PROLOGUE_SIZE 42
9696
#define EPILOGUE_SIZE 21
9797
#define OPCODE_BUFSIZE (PROLOGUE_SIZE + 1 + EPILOGUE_SIZE)
9898
u8 opcodes[OPCODE_BUFSIZE];
99+
unsigned long prologue = regs->ip - PROLOGUE_SIZE;
100+
bool bad_ip;
99101

100-
if (probe_kernel_read(opcodes, rip - PROLOGUE_SIZE, OPCODE_BUFSIZE)) {
102+
/*
103+
* Make sure userspace isn't trying to trick us into dumping kernel
104+
* memory by pointing the userspace instruction pointer at it.
105+
*/
106+
bad_ip = user_mode(regs) &&
107+
__chk_range_not_ok(prologue, OPCODE_BUFSIZE, TASK_SIZE_MAX);
108+
109+
if (bad_ip || probe_kernel_read(opcodes, (u8 *)prologue,
110+
OPCODE_BUFSIZE)) {
101111
printk("%sCode: Bad RIP value.\n", loglvl);
102112
} else {
103113
printk("%sCode: %" __stringify(PROLOGUE_SIZE) "ph <%02x> %"
@@ -113,7 +123,7 @@ void show_ip(struct pt_regs *regs, const char *loglvl)
113123
#else
114124
printk("%sRIP: %04x:%pS\n", loglvl, (int)regs->cs, (void *)regs->ip);
115125
#endif
116-
show_opcodes((u8 *)regs->ip, loglvl);
126+
show_opcodes(regs, loglvl);
117127
}
118128

119129
void show_iret_regs(struct pt_regs *regs)

arch/x86/mm/fault.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -837,7 +837,7 @@ show_signal_msg(struct pt_regs *regs, unsigned long error_code,
837837

838838
printk(KERN_CONT "\n");
839839

840-
show_opcodes((u8 *)regs->ip, loglvl);
840+
show_opcodes(regs, loglvl);
841841
}
842842

843843
static void

0 commit comments

Comments
 (0)