diff options
Diffstat (limited to 'arch/x86_64/kernel/traps.c')
-rw-r--r-- | arch/x86_64/kernel/traps.c | 95 |
1 files changed, 52 insertions, 43 deletions
diff --git a/arch/x86_64/kernel/traps.c b/arch/x86_64/kernel/traps.c index 7819022a8db5..b54ccc07f379 100644 --- a/arch/x86_64/kernel/traps.c +++ b/arch/x86_64/kernel/traps.c | |||
@@ -30,9 +30,10 @@ | |||
30 | #include <linux/kprobes.h> | 30 | #include <linux/kprobes.h> |
31 | #include <linux/kexec.h> | 31 | #include <linux/kexec.h> |
32 | #include <linux/unwind.h> | 32 | #include <linux/unwind.h> |
33 | #include <linux/uaccess.h> | ||
34 | #include <linux/bug.h> | ||
33 | 35 | ||
34 | #include <asm/system.h> | 36 | #include <asm/system.h> |
35 | #include <asm/uaccess.h> | ||
36 | #include <asm/io.h> | 37 | #include <asm/io.h> |
37 | #include <asm/atomic.h> | 38 | #include <asm/atomic.h> |
38 | #include <asm/debugreg.h> | 39 | #include <asm/debugreg.h> |
@@ -108,7 +109,7 @@ static inline void preempt_conditional_cli(struct pt_regs *regs) | |||
108 | preempt_enable_no_resched(); | 109 | preempt_enable_no_resched(); |
109 | } | 110 | } |
110 | 111 | ||
111 | static int kstack_depth_to_print = 12; | 112 | int kstack_depth_to_print = 12; |
112 | #ifdef CONFIG_STACK_UNWIND | 113 | #ifdef CONFIG_STACK_UNWIND |
113 | static int call_trace = 1; | 114 | static int call_trace = 1; |
114 | #else | 115 | #else |
@@ -225,16 +226,25 @@ static int dump_trace_unwind(struct unwind_frame_info *info, void *context) | |||
225 | { | 226 | { |
226 | struct ops_and_data *oad = (struct ops_and_data *)context; | 227 | struct ops_and_data *oad = (struct ops_and_data *)context; |
227 | int n = 0; | 228 | int n = 0; |
229 | unsigned long sp = UNW_SP(info); | ||
228 | 230 | ||
231 | if (arch_unw_user_mode(info)) | ||
232 | return -1; | ||
229 | while (unwind(info) == 0 && UNW_PC(info)) { | 233 | while (unwind(info) == 0 && UNW_PC(info)) { |
230 | n++; | 234 | n++; |
231 | oad->ops->address(oad->data, UNW_PC(info)); | 235 | oad->ops->address(oad->data, UNW_PC(info)); |
232 | if (arch_unw_user_mode(info)) | 236 | if (arch_unw_user_mode(info)) |
233 | break; | 237 | break; |
238 | if ((sp & ~(PAGE_SIZE - 1)) == (UNW_SP(info) & ~(PAGE_SIZE - 1)) | ||
239 | && sp > UNW_SP(info)) | ||
240 | break; | ||
241 | sp = UNW_SP(info); | ||
234 | } | 242 | } |
235 | return n; | 243 | return n; |
236 | } | 244 | } |
237 | 245 | ||
246 | #define MSG(txt) ops->warning(data, txt) | ||
247 | |||
238 | /* | 248 | /* |
239 | * x86-64 can have upto three kernel stacks: | 249 | * x86-64 can have upto three kernel stacks: |
240 | * process stack | 250 | * process stack |
@@ -242,12 +252,20 @@ static int dump_trace_unwind(struct unwind_frame_info *info, void *context) | |||
242 | * severe exception (double fault, nmi, stack fault, debug, mce) hardware stack | 252 | * severe exception (double fault, nmi, stack fault, debug, mce) hardware stack |
243 | */ | 253 | */ |
244 | 254 | ||
245 | void dump_trace(struct task_struct *tsk, struct pt_regs *regs, unsigned long * stack, | 255 | static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) |
256 | { | ||
257 | void *t = (void *)tinfo; | ||
258 | return p > t && p < t + THREAD_SIZE - 3; | ||
259 | } | ||
260 | |||
261 | void dump_trace(struct task_struct *tsk, struct pt_regs *regs, | ||
262 | unsigned long *stack, | ||
246 | struct stacktrace_ops *ops, void *data) | 263 | struct stacktrace_ops *ops, void *data) |
247 | { | 264 | { |
248 | const unsigned cpu = smp_processor_id(); | 265 | const unsigned cpu = get_cpu(); |
249 | unsigned long *irqstack_end = (unsigned long *)cpu_pda(cpu)->irqstackptr; | 266 | unsigned long *irqstack_end = (unsigned long*)cpu_pda(cpu)->irqstackptr; |
250 | unsigned used = 0; | 267 | unsigned used = 0; |
268 | struct thread_info *tinfo; | ||
251 | 269 | ||
252 | if (!tsk) | 270 | if (!tsk) |
253 | tsk = current; | 271 | tsk = current; |
@@ -261,28 +279,30 @@ void dump_trace(struct task_struct *tsk, struct pt_regs *regs, unsigned long * s | |||
261 | if (unwind_init_frame_info(&info, tsk, regs) == 0) | 279 | if (unwind_init_frame_info(&info, tsk, regs) == 0) |
262 | unw_ret = dump_trace_unwind(&info, &oad); | 280 | unw_ret = dump_trace_unwind(&info, &oad); |
263 | } else if (tsk == current) | 281 | } else if (tsk == current) |
264 | unw_ret = unwind_init_running(&info, dump_trace_unwind, &oad); | 282 | unw_ret = unwind_init_running(&info, dump_trace_unwind, |
283 | &oad); | ||
265 | else { | 284 | else { |
266 | if (unwind_init_blocked(&info, tsk) == 0) | 285 | if (unwind_init_blocked(&info, tsk) == 0) |
267 | unw_ret = dump_trace_unwind(&info, &oad); | 286 | unw_ret = dump_trace_unwind(&info, &oad); |
268 | } | 287 | } |
269 | if (unw_ret > 0) { | 288 | if (unw_ret > 0) { |
270 | if (call_trace == 1 && !arch_unw_user_mode(&info)) { | 289 | if (call_trace == 1 && !arch_unw_user_mode(&info)) { |
271 | ops->warning_symbol(data, "DWARF2 unwinder stuck at %s\n", | 290 | ops->warning_symbol(data, |
291 | "DWARF2 unwinder stuck at %s", | ||
272 | UNW_PC(&info)); | 292 | UNW_PC(&info)); |
273 | if ((long)UNW_SP(&info) < 0) { | 293 | if ((long)UNW_SP(&info) < 0) { |
274 | ops->warning(data, "Leftover inexact backtrace:\n"); | 294 | MSG("Leftover inexact backtrace:"); |
275 | stack = (unsigned long *)UNW_SP(&info); | 295 | stack = (unsigned long *)UNW_SP(&info); |
276 | if (!stack) | 296 | if (!stack) |
277 | return; | 297 | goto out; |
278 | } else | 298 | } else |
279 | ops->warning(data, "Full inexact backtrace again:\n"); | 299 | MSG("Full inexact backtrace again:"); |
280 | } else if (call_trace >= 1) | 300 | } else if (call_trace >= 1) |
281 | return; | 301 | goto out; |
282 | else | 302 | else |
283 | ops->warning(data, "Full inexact backtrace again:\n"); | 303 | MSG("Full inexact backtrace again:"); |
284 | } else | 304 | } else |
285 | ops->warning(data, "Inexact backtrace:\n"); | 305 | MSG("Inexact backtrace:"); |
286 | } | 306 | } |
287 | if (!stack) { | 307 | if (!stack) { |
288 | unsigned long dummy; | 308 | unsigned long dummy; |
@@ -299,9 +319,9 @@ void dump_trace(struct task_struct *tsk, struct pt_regs *regs, unsigned long * s | |||
299 | #define HANDLE_STACK(cond) \ | 319 | #define HANDLE_STACK(cond) \ |
300 | do while (cond) { \ | 320 | do while (cond) { \ |
301 | unsigned long addr = *stack++; \ | 321 | unsigned long addr = *stack++; \ |
302 | if (oops_in_progress ? \ | 322 | /* Use unlocked access here because except for NMIs \ |
303 | __kernel_text_address(addr) : \ | 323 | we should be already protected against module unloads */ \ |
304 | kernel_text_address(addr)) { \ | 324 | if (__kernel_text_address(addr)) { \ |
305 | /* \ | 325 | /* \ |
306 | * If the address is either in the text segment of the \ | 326 | * If the address is either in the text segment of the \ |
307 | * kernel, or in the region which contains vmalloc'ed \ | 327 | * kernel, or in the region which contains vmalloc'ed \ |
@@ -364,8 +384,11 @@ void dump_trace(struct task_struct *tsk, struct pt_regs *regs, unsigned long * s | |||
364 | /* | 384 | /* |
365 | * This handles the process stack: | 385 | * This handles the process stack: |
366 | */ | 386 | */ |
367 | HANDLE_STACK (((long) stack & (THREAD_SIZE-1)) != 0); | 387 | tinfo = current_thread_info(); |
388 | HANDLE_STACK (valid_stack_ptr(tinfo, stack)); | ||
368 | #undef HANDLE_STACK | 389 | #undef HANDLE_STACK |
390 | out: | ||
391 | put_cpu(); | ||
369 | } | 392 | } |
370 | EXPORT_SYMBOL(dump_trace); | 393 | EXPORT_SYMBOL(dump_trace); |
371 | 394 | ||
@@ -502,30 +525,15 @@ bad: | |||
502 | printk("\n"); | 525 | printk("\n"); |
503 | } | 526 | } |
504 | 527 | ||
505 | void handle_BUG(struct pt_regs *regs) | 528 | int is_valid_bugaddr(unsigned long rip) |
506 | { | 529 | { |
507 | struct bug_frame f; | 530 | unsigned short ud2; |
508 | long len; | ||
509 | const char *prefix = ""; | ||
510 | 531 | ||
511 | if (user_mode(regs)) | 532 | if (__copy_from_user(&ud2, (const void __user *) rip, sizeof(ud2))) |
512 | return; | 533 | return 0; |
513 | if (__copy_from_user(&f, (const void __user *) regs->rip, | 534 | |
514 | sizeof(struct bug_frame))) | 535 | return ud2 == 0x0b0f; |
515 | return; | 536 | } |
516 | if (f.filename >= 0 || | ||
517 | f.ud2[0] != 0x0f || f.ud2[1] != 0x0b) | ||
518 | return; | ||
519 | len = __strnlen_user((char *)(long)f.filename, PATH_MAX) - 1; | ||
520 | if (len < 0 || len >= PATH_MAX) | ||
521 | f.filename = (int)(long)"unmapped filename"; | ||
522 | else if (len > 50) { | ||
523 | f.filename += len - 50; | ||
524 | prefix = "..."; | ||
525 | } | ||
526 | printk("----------- [cut here ] --------- [please bite here ] ---------\n"); | ||
527 | printk(KERN_ALERT "Kernel BUG at %s%.50s:%d\n", prefix, (char *)(long)f.filename, f.line); | ||
528 | } | ||
529 | 537 | ||
530 | #ifdef CONFIG_BUG | 538 | #ifdef CONFIG_BUG |
531 | void out_of_line_bug(void) | 539 | void out_of_line_bug(void) |
@@ -605,7 +613,9 @@ void die(const char * str, struct pt_regs * regs, long err) | |||
605 | { | 613 | { |
606 | unsigned long flags = oops_begin(); | 614 | unsigned long flags = oops_begin(); |
607 | 615 | ||
608 | handle_BUG(regs); | 616 | if (!user_mode(regs)) |
617 | report_bug(regs->rip); | ||
618 | |||
609 | __die(str, regs, err); | 619 | __die(str, regs, err); |
610 | oops_end(flags); | 620 | oops_end(flags); |
611 | do_exit(SIGSEGV); | 621 | do_exit(SIGSEGV); |
@@ -772,8 +782,7 @@ mem_parity_error(unsigned char reason, struct pt_regs * regs) | |||
772 | { | 782 | { |
773 | printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x.\n", | 783 | printk(KERN_EMERG "Uhhuh. NMI received for unknown reason %02x.\n", |
774 | reason); | 784 | reason); |
775 | printk(KERN_EMERG "You probably have a hardware problem with your " | 785 | printk(KERN_EMERG "You have some hardware problem, likely on the PCI bus.\n"); |
776 | "RAM chips\n"); | ||
777 | 786 | ||
778 | if (panic_on_unrecovered_nmi) | 787 | if (panic_on_unrecovered_nmi) |
779 | panic("NMI: Not continuing"); | 788 | panic("NMI: Not continuing"); |