diff options
Diffstat (limited to 'arch/x86/kernel/dumpstack.c')
-rw-r--r-- | arch/x86/kernel/dumpstack.c | 31 |
1 files changed, 20 insertions, 11 deletions
diff --git a/arch/x86/kernel/dumpstack.c b/arch/x86/kernel/dumpstack.c index 9c8652974f8e..2b5886401e5f 100644 --- a/arch/x86/kernel/dumpstack.c +++ b/arch/x86/kernel/dumpstack.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/bug.h> | 17 | #include <linux/bug.h> |
18 | #include <linux/nmi.h> | 18 | #include <linux/nmi.h> |
19 | #include <linux/sysfs.h> | 19 | #include <linux/sysfs.h> |
20 | #include <linux/kasan.h> | ||
20 | 21 | ||
21 | #include <asm/cpu_entry_area.h> | 22 | #include <asm/cpu_entry_area.h> |
22 | #include <asm/stacktrace.h> | 23 | #include <asm/stacktrace.h> |
@@ -89,14 +90,24 @@ static void printk_stack_address(unsigned long address, int reliable, | |||
89 | * Thus, the 2/3rds prologue and 64 byte OPCODE_BUFSIZE is just a random | 90 | * Thus, the 2/3rds prologue and 64 byte OPCODE_BUFSIZE is just a random |
90 | * guesstimate in attempt to achieve all of the above. | 91 | * guesstimate in attempt to achieve all of the above. |
91 | */ | 92 | */ |
92 | void show_opcodes(u8 *rip, const char *loglvl) | 93 | void show_opcodes(struct pt_regs *regs, const char *loglvl) |
93 | { | 94 | { |
94 | #define PROLOGUE_SIZE 42 | 95 | #define PROLOGUE_SIZE 42 |
95 | #define EPILOGUE_SIZE 21 | 96 | #define EPILOGUE_SIZE 21 |
96 | #define OPCODE_BUFSIZE (PROLOGUE_SIZE + 1 + EPILOGUE_SIZE) | 97 | #define OPCODE_BUFSIZE (PROLOGUE_SIZE + 1 + EPILOGUE_SIZE) |
97 | u8 opcodes[OPCODE_BUFSIZE]; | 98 | u8 opcodes[OPCODE_BUFSIZE]; |
99 | unsigned long prologue = regs->ip - PROLOGUE_SIZE; | ||
100 | bool bad_ip; | ||
98 | 101 | ||
99 | 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)) { | ||
100 | printk("%sCode: Bad RIP value.\n", loglvl); | 111 | printk("%sCode: Bad RIP value.\n", loglvl); |
101 | } else { | 112 | } else { |
102 | printk("%sCode: %" __stringify(PROLOGUE_SIZE) "ph <%02x> %" | 113 | printk("%sCode: %" __stringify(PROLOGUE_SIZE) "ph <%02x> %" |
@@ -112,7 +123,7 @@ void show_ip(struct pt_regs *regs, const char *loglvl) | |||
112 | #else | 123 | #else |
113 | printk("%sRIP: %04x:%pS\n", loglvl, (int)regs->cs, (void *)regs->ip); | 124 | printk("%sRIP: %04x:%pS\n", loglvl, (int)regs->cs, (void *)regs->ip); |
114 | #endif | 125 | #endif |
115 | show_opcodes((u8 *)regs->ip, loglvl); | 126 | show_opcodes(regs, loglvl); |
116 | } | 127 | } |
117 | 128 | ||
118 | void show_iret_regs(struct pt_regs *regs) | 129 | void show_iret_regs(struct pt_regs *regs) |
@@ -135,7 +146,7 @@ static void show_regs_if_on_stack(struct stack_info *info, struct pt_regs *regs, | |||
135 | * they can be printed in the right context. | 146 | * they can be printed in the right context. |
136 | */ | 147 | */ |
137 | if (!partial && on_stack(info, regs, sizeof(*regs))) { | 148 | if (!partial && on_stack(info, regs, sizeof(*regs))) { |
138 | __show_regs(regs, 0); | 149 | __show_regs(regs, SHOW_REGS_SHORT); |
139 | 150 | ||
140 | } else if (partial && on_stack(info, (void *)regs + IRET_FRAME_OFFSET, | 151 | } else if (partial && on_stack(info, (void *)regs + IRET_FRAME_OFFSET, |
141 | IRET_FRAME_SIZE)) { | 152 | IRET_FRAME_SIZE)) { |
@@ -333,7 +344,7 @@ void oops_end(unsigned long flags, struct pt_regs *regs, int signr) | |||
333 | oops_exit(); | 344 | oops_exit(); |
334 | 345 | ||
335 | /* Executive summary in case the oops scrolled away */ | 346 | /* Executive summary in case the oops scrolled away */ |
336 | __show_regs(&exec_summary_regs, true); | 347 | __show_regs(&exec_summary_regs, SHOW_REGS_ALL); |
337 | 348 | ||
338 | if (!signr) | 349 | if (!signr) |
339 | return; | 350 | return; |
@@ -346,7 +357,10 @@ void oops_end(unsigned long flags, struct pt_regs *regs, int signr) | |||
346 | * We're not going to return, but we might be on an IST stack or | 357 | * We're not going to return, but we might be on an IST stack or |
347 | * have very little stack space left. Rewind the stack and kill | 358 | * have very little stack space left. Rewind the stack and kill |
348 | * the task. | 359 | * the task. |
360 | * Before we rewind the stack, we have to tell KASAN that we're going to | ||
361 | * reuse the task stack and that existing poisons are invalid. | ||
349 | */ | 362 | */ |
363 | kasan_unpoison_task_stack(current); | ||
350 | rewind_stack_do_exit(signr); | 364 | rewind_stack_do_exit(signr); |
351 | } | 365 | } |
352 | NOKPROBE_SYMBOL(oops_end); | 366 | NOKPROBE_SYMBOL(oops_end); |
@@ -393,14 +407,9 @@ void die(const char *str, struct pt_regs *regs, long err) | |||
393 | 407 | ||
394 | void show_regs(struct pt_regs *regs) | 408 | void show_regs(struct pt_regs *regs) |
395 | { | 409 | { |
396 | bool all = true; | ||
397 | |||
398 | show_regs_print_info(KERN_DEFAULT); | 410 | show_regs_print_info(KERN_DEFAULT); |
399 | 411 | ||
400 | if (IS_ENABLED(CONFIG_X86_32)) | 412 | __show_regs(regs, user_mode(regs) ? SHOW_REGS_USER : SHOW_REGS_ALL); |
401 | all = !user_mode(regs); | ||
402 | |||
403 | __show_regs(regs, all); | ||
404 | 413 | ||
405 | /* | 414 | /* |
406 | * When in-kernel, we also print out the stack at the time of the fault.. | 415 | * When in-kernel, we also print out the stack at the time of the fault.. |