Skip to content

Commit a1a371c

Browse files
amlutoIngo Molnar
authored andcommitted
x86/fault: Decode page fault OOPSes better
One of Linus' favorite hobbies seems to be looking at OOPSes and decoding the error code in his head. This is not one of my favorite hobbies :) Teach the page fault OOPS hander to decode the error code. If it's a !USER fault from user mode, print an explicit note to that effect and print out the addresses of various tables that might cause such an error. With this patch applied, if I intentionally point the LDT at 0x0 and run the x86 selftests, I get: BUG: unable to handle kernel NULL pointer dereference at 0000000000000000 HW error: normal kernel read fault This was a system access from user code IDT: 0xfffffe0000000000 (limit=0xfff) GDT: 0xfffffe0000001000 (limit=0x7f) LDTR: 0x50 -- base=0x0 limit=0xfff7 TR: 0x40 -- base=0xfffffe0000003000 limit=0x206f PGD 800000000456e067 P4D 800000000456e067 PUD 4623067 PMD 0 SMP PTI CPU: 0 PID: 153 Comm: ldt_gdt_64 Not tainted 4.19.0+ #1317 Hardware name: ... RIP: 0033:0x401454 Signed-off-by: Andy Lutomirski <[email protected]> Cc: Borislav Petkov <[email protected]> Cc: Dave Hansen <[email protected]> Cc: H. Peter Anvin <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Rik van Riel <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Yu-cheng Yu <[email protected]> Link: http://lkml.kernel.org/r/11212acb25980cd1b3030875cd9502414fbb214d.1542841400.git.luto@kernel.org Signed-off-by: Ingo Molnar <[email protected]>
1 parent af2ebdc commit a1a371c

File tree

1 file changed

+84
-0
lines changed

1 file changed

+84
-0
lines changed

arch/x86/mm/fault.c

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <asm/vm86.h> /* struct vm86 */
2828
#include <asm/mmu_context.h> /* vma_pkey() */
2929
#include <asm/efi.h> /* efi_recover_from_page_fault()*/
30+
#include <asm/desc.h> /* store_idt(), ... */
3031

3132
#define CREATE_TRACE_POINTS
3233
#include <asm/trace/exceptions.h>
@@ -571,10 +572,53 @@ static int is_f00f_bug(struct pt_regs *regs, unsigned long address)
571572
return 0;
572573
}
573574

575+
static void show_ldttss(const struct desc_ptr *gdt, const char *name, u16 index)
576+
{
577+
u32 offset = (index >> 3) * sizeof(struct desc_struct);
578+
unsigned long addr;
579+
struct ldttss_desc desc;
580+
581+
if (index == 0) {
582+
pr_alert("%s: NULL\n", name);
583+
return;
584+
}
585+
586+
if (offset + sizeof(struct ldttss_desc) >= gdt->size) {
587+
pr_alert("%s: 0x%hx -- out of bounds\n", name, index);
588+
return;
589+
}
590+
591+
if (probe_kernel_read(&desc, (void *)(gdt->address + offset),
592+
sizeof(struct ldttss_desc))) {
593+
pr_alert("%s: 0x%hx -- GDT entry is not readable\n",
594+
name, index);
595+
return;
596+
}
597+
598+
addr = desc.base0 | (desc.base1 << 16) | (desc.base2 << 24);
599+
#ifdef CONFIG_X86_64
600+
addr |= ((u64)desc.base3 << 32);
601+
#endif
602+
pr_alert("%s: 0x%hx -- base=0x%lx limit=0x%x\n",
603+
name, index, addr, (desc.limit0 | (desc.limit1 << 16)));
604+
}
605+
606+
static void errstr(unsigned long ec, char *buf, unsigned long mask,
607+
const char *txt)
608+
{
609+
if (ec & mask) {
610+
if (buf[0])
611+
strcat(buf, " ");
612+
strcat(buf, txt);
613+
}
614+
}
615+
574616
static void
575617
show_fault_oops(struct pt_regs *regs, unsigned long error_code,
576618
unsigned long address)
577619
{
620+
char errtxt[64];
621+
578622
if (!oops_may_print())
579623
return;
580624

@@ -602,6 +646,46 @@ show_fault_oops(struct pt_regs *regs, unsigned long error_code,
602646
address < PAGE_SIZE ? "NULL pointer dereference" : "paging request",
603647
(void *)address);
604648

649+
errtxt[0] = 0;
650+
errstr(error_code, errtxt, X86_PF_PROT, "PROT");
651+
errstr(error_code, errtxt, X86_PF_WRITE, "WRITE");
652+
errstr(error_code, errtxt, X86_PF_USER, "USER");
653+
errstr(error_code, errtxt, X86_PF_RSVD, "RSVD");
654+
errstr(error_code, errtxt, X86_PF_INSTR, "INSTR");
655+
errstr(error_code, errtxt, X86_PF_PK, "PK");
656+
pr_alert("HW error: %s\n", error_code ? errtxt :
657+
"normal kernel read fault");
658+
if (!(error_code & X86_PF_USER) && user_mode(regs)) {
659+
struct desc_ptr idt, gdt;
660+
u16 ldtr, tr;
661+
662+
pr_alert("This was a system access from user code\n");
663+
664+
/*
665+
* This can happen for quite a few reasons. The more obvious
666+
* ones are faults accessing the GDT, or LDT. Perhaps
667+
* surprisingly, if the CPU tries to deliver a benign or
668+
* contributory exception from user code and gets a page fault
669+
* during delivery, the page fault can be delivered as though
670+
* it originated directly from user code. This could happen
671+
* due to wrong permissions on the IDT, GDT, LDT, TSS, or
672+
* kernel or IST stack.
673+
*/
674+
store_idt(&idt);
675+
676+
/* Usable even on Xen PV -- it's just slow. */
677+
native_store_gdt(&gdt);
678+
679+
pr_alert("IDT: 0x%lx (limit=0x%hx) GDT: 0x%lx (limit=0x%hx)\n",
680+
idt.address, idt.size, gdt.address, gdt.size);
681+
682+
store_ldt(ldtr);
683+
show_ldttss(&gdt, "LDTR", ldtr);
684+
685+
store_tr(tr);
686+
show_ldttss(&gdt, "TR", tr);
687+
}
688+
605689
dump_pagetable(address);
606690
}
607691

0 commit comments

Comments
 (0)