From b97e7fd4e8268d5c46f1b30b41ce1f6ca9ceb216 Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke Date: Mon, 27 Jul 2020 19:00:19 +0900 Subject: [PATCH 1/4] symbols: Add linux_banner_vmlinux in symbol table Add linux_banner_vmlinux in symbol table, which we'll later use in calc_kaslr_offset() to do a sanity check in calculation of kaslr_offset and phys_base. Signed-off-by: HATAYAMA Daisuke --- defs.h | 1 + symbols.c | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/defs.h b/defs.h index d7adb23b86d5..17e98763362b 100644 --- a/defs.h +++ b/defs.h @@ -2714,6 +2714,7 @@ struct symbol_table_data { ulong pti_init_vmlinux; ulong kaiser_init_vmlinux; int kernel_symbol_type; + ulong linux_banner_vmlinux; }; /* flags for st */ diff --git a/symbols.c b/symbols.c index 3b1f08af43ff..b9de4a179d93 100644 --- a/symbols.c +++ b/symbols.c @@ -3226,6 +3226,11 @@ dump_symbol_table(void) fprintf(fp, " kaiser_init_vmlinux: (unused)\n"); } + if (SADUMP_DUMPFILE()) + fprintf(fp, "linux_banner_vmlinux: %lx\n", st->linux_banner_vmlinux); + else + fprintf(fp, "linux_banner_vmlinux: (unused)\n"); + fprintf(fp, " symval_hash[%d]: %lx\n", SYMVAL_HASH, (ulong)&st->symval_hash[0]); @@ -12687,6 +12692,11 @@ numeric_forward(const void *P_x, const void *P_y) else if (STREQ(y->name, "idt_table")) st->idt_table_vmlinux = valueof(y); + if (STREQ(x->name, "linux_banner")) + st->linux_banner_vmlinux = valueof(x); + else if (STREQ(y->name, "linux_banner")) + st->linux_banner_vmlinux = valueof(y); + if (STREQ(x->name, "saved_command_line")) st->saved_command_line_vmlinux = valueof(x); else if (STREQ(y->name, "saved_command_line")) -- 2.7.4 From d494fabe99b90cea8d717a90951e44e6dbda84bb Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke Date: Mon, 27 Jul 2020 19:00:20 +0900 Subject: [PATCH 2/4] symbols: fix initialization of st->{pti_init, kaiser}_vmlinux In numeric_forward(), care must be taken both for x- and y- positions, but either of kaiser_init and pti_init is only for x- or y- position only. Fix this. Also, move the code in an appropriate position according to each symbol name in the alphabetical order. Signed-off-by: HATAYAMA Daisuke --- symbols.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/symbols.c b/symbols.c index b9de4a179d93..2fecaee093a2 100644 --- a/symbols.c +++ b/symbols.c @@ -12692,20 +12692,25 @@ numeric_forward(const void *P_x, const void *P_y) else if (STREQ(y->name, "idt_table")) st->idt_table_vmlinux = valueof(y); + if (STREQ(x->name, "kaiser_init")) + st->kaiser_init_vmlinux = valueof(x); + else if (STREQ(y->name, "kaiser_init")) + st->kaiser_init_vmlinux = valueof(y); + if (STREQ(x->name, "linux_banner")) st->linux_banner_vmlinux = valueof(x); else if (STREQ(y->name, "linux_banner")) st->linux_banner_vmlinux = valueof(y); + if (STREQ(x->name, "pti_init")) + st->pti_init_vmlinux = valueof(x); + else if (STREQ(y->name, "pti_init")) + st->pti_init_vmlinux = valueof(y); + if (STREQ(x->name, "saved_command_line")) st->saved_command_line_vmlinux = valueof(x); else if (STREQ(y->name, "saved_command_line")) st->saved_command_line_vmlinux = valueof(y); - - if (STREQ(x->name, "pti_init")) - st->pti_init_vmlinux = valueof(x); - else if (STREQ(y->name, "kaiser_init")) - st->kaiser_init_vmlinux = valueof(y); } xs = bfd_get_section(x); -- 2.7.4 From ff45c8da8cafed350940b1a56dce65f58051db5e Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke Date: Mon, 27 Jul 2020 19:00:22 +0900 Subject: [PATCH 3/4] kaslr: cleanup how to set values to the arguments of calc_kaslr_offset() Setting values of the arguments of calc_kaslr_offset() should be done at the end of the function. Currently, they are set in the middle where their values could still be changed according to get_kaslr_offset_from_vmcoreinfo(). This behavior will be problematic in the later commits when we implement a trial-and-error approach because the value of kaslr_offset could be passed to the outside of calc_kaslr_offset() unexpectedly. Thus, fix this first. Signed-off-by: HATAYAMA Daisuke --- kaslr_helper.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/kaslr_helper.c b/kaslr_helper.c index fe5909caa937..acbb5c2692e2 100644 --- a/kaslr_helper.c +++ b/kaslr_helper.c @@ -394,10 +394,11 @@ quit: #define PTI_USER_PGTABLE_MASK (1 << PTI_USER_PGTABLE_BIT) #define CR3_PCID_MASK 0xFFFull int -calc_kaslr_offset(ulong *kaslr_offset, ulong *phys_base) +calc_kaslr_offset(ulong *ko, ulong *pb) { uint64_t cr3 = 0, idtr = 0, pgd = 0, idtr_paddr; ulong divide_error_vmcore; + ulong kaslr_offset, phys_base; ulong kaslr_offset_kdump, phys_base_kdump; int ret = FALSE; int verbose = CRASHDEBUG(1)? 1: 0; @@ -445,9 +446,9 @@ calc_kaslr_offset(ulong *kaslr_offset, ulong *phys_base) /* Now we can calculate kaslr_offset and phys_base */ divide_error_vmcore = get_vec0_addr(idtr_paddr); - *kaslr_offset = divide_error_vmcore - st->divide_error_vmlinux; - *phys_base = idtr_paddr - - (st->idt_table_vmlinux + *kaslr_offset - __START_KERNEL_map); + kaslr_offset = divide_error_vmcore - st->divide_error_vmlinux; + phys_base = idtr_paddr - + (st->idt_table_vmlinux + kaslr_offset - __START_KERNEL_map); if (CRASHDEBUG(1)) { fprintf(fp, "calc_kaslr_offset: idtr=%lx\n", idtr); @@ -465,9 +466,9 @@ calc_kaslr_offset(ulong *kaslr_offset, ulong *phys_base) * from vmcoreinfo */ if (get_kaslr_offset_from_vmcoreinfo( - *kaslr_offset, &kaslr_offset_kdump, &phys_base_kdump)) { - *kaslr_offset = kaslr_offset_kdump; - *phys_base = phys_base_kdump; + kaslr_offset, &kaslr_offset_kdump, &phys_base_kdump)) { + kaslr_offset = kaslr_offset_kdump; + phys_base = phys_base_kdump; } else if (CRASHDEBUG(1)) { fprintf(fp, "kaslr_helper: failed to determine which kernel was running at crash,\n"); fprintf(fp, "kaslr_helper: asssuming the kdump 1st kernel.\n"); @@ -475,10 +476,13 @@ calc_kaslr_offset(ulong *kaslr_offset, ulong *phys_base) if (CRASHDEBUG(1)) { fprintf(fp, "calc_kaslr_offset: kaslr_offset=%lx\n", - *kaslr_offset); - fprintf(fp, "calc_kaslr_offset: phys_base=%lx\n", *phys_base); + kaslr_offset); + fprintf(fp, "calc_kaslr_offset: phys_base=%lx\n", phys_base); } + *ko = kaslr_offset; + *pb = phys_base; + ret = TRUE; quit: vt->kernel_pgd[0] = 0; -- 2.7.4 From 8b50d94ada21f403665a5e562f40191f111e0313 Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke Date: Mon, 27 Jul 2020 19:00:23 +0900 Subject: [PATCH 4/4] kaslr: fix failure of calculating kaslr_offset due to an sadump format restriction We faced recently a memory dump collected by sadump where unused part of register values are non-zero. For the crash dump, calculating kaslr_offset fails because it is based on the assumption that unused part of register values in the sadump format are always zero cleared. The problem is that used and unused part of register values are rigorously indistinguishable in the sadump format. Although there is kernel data structure that represents a map between logical cpu numbers and lapic ids, they cannot be used in order to calculate kaslr_offset. To fix this, we have no choice but use a trial-and-error approach: try to use each entry of register values in order until we find a good pair of cr3 and idtr by which we can refer to linux_banner symbol as expected. This fix is for the sadump specific issue, so there is no functional change for the other crash dump formats. [ lijiang: adjust the code indent. ] Signed-off-by: HATAYAMA Daisuke --- kaslr_helper.c | 39 +++++++++++++++++++++++++++++++++++---- sadump.c | 52 ++++++++++++++++++++++++++++------------------------ 2 files changed, 63 insertions(+), 28 deletions(-) diff --git a/kaslr_helper.c b/kaslr_helper.c index acbb5c2692e2..bb19e548d04e 100644 --- a/kaslr_helper.c +++ b/kaslr_helper.c @@ -406,6 +406,7 @@ calc_kaslr_offset(ulong *ko, ulong *pb) if (!machine_type("X86_64")) return FALSE; +retry: if (SADUMP_DUMPFILE()) { if (!sadump_get_cr3_idtr(&cr3, &idtr)) return FALSE; @@ -437,12 +438,20 @@ calc_kaslr_offset(ulong *ko, ulong *pb) machdep->machspec->pgdir_shift = PGDIR_SHIFT; machdep->machspec->ptrs_per_pgd = PTRS_PER_PGD; if (!readmem(pgd, PHYSADDR, machdep->pgd, PAGESIZE(), - "pgd", RETURN_ON_ERROR)) - goto quit; + "pgd", RETURN_ON_ERROR)) { + if (SADUMP_DUMPFILE()) + goto retry; + else + goto quit; + } /* Convert virtual address of IDT table to physical address */ - if (!kvtop(NULL, idtr, &idtr_paddr, verbose)) - goto quit; + if (!kvtop(NULL, idtr, &idtr_paddr, verbose)) { + if (SADUMP_DUMPFILE()) + goto retry; + else + goto quit; + } /* Now we can calculate kaslr_offset and phys_base */ divide_error_vmcore = get_vec0_addr(idtr_paddr); @@ -450,6 +459,28 @@ calc_kaslr_offset(ulong *ko, ulong *pb) phys_base = idtr_paddr - (st->idt_table_vmlinux + kaslr_offset - __START_KERNEL_map); + if (SADUMP_DUMPFILE()) { + char buf[sizeof("Linux version")]; + ulong linux_banner_paddr; + + if (!kvtop(NULL, + st->linux_banner_vmlinux + kaslr_offset, + &linux_banner_paddr, + verbose)) + goto retry; + + if (!readmem(linux_banner_paddr, + PHYSADDR, + buf, + sizeof(buf), + "linux_banner", + RETURN_ON_ERROR)) + goto retry; + + if (!STRNEQ(buf, "Linux version")) + goto retry; + } + if (CRASHDEBUG(1)) { fprintf(fp, "calc_kaslr_offset: idtr=%lx\n", idtr); fprintf(fp, "calc_kaslr_offset: pgd=%lx\n", pgd); diff --git a/sadump.c b/sadump.c index 35f7cf0fcf8f..009e17a4a44a 100644 --- a/sadump.c +++ b/sadump.c @@ -1664,29 +1664,32 @@ get_sadump_data(void) static int get_sadump_smram_cpu_state_any(struct sadump_smram_cpu_state *smram) { - ulong offset; - struct sadump_header *sh = sd->dump_header; - int apicid; - struct sadump_smram_cpu_state scs, zero; - - offset = sd->sub_hdr_offset + sizeof(uint32_t) + - sd->dump_header->nr_cpus * sizeof(struct sadump_apic_state); - - memset(&zero, 0, sizeof(zero)); - - for (apicid = 0; apicid < sh->nr_cpus; ++apicid) { - if (!read_device(&scs, sizeof(scs), &offset)) { - error(INFO, "sadump: cannot read sub header " - "cpu_state\n"); - return FALSE; - } - if (memcmp(&scs, &zero, sizeof(scs)) != 0) { - *smram = scs; - return TRUE; - } - } - - return FALSE; + ulong offset; + struct sadump_header *sh = sd->dump_header; + static int apicid; + struct sadump_smram_cpu_state scs; + + if (apicid >= sh->nr_cpus) + return FALSE; + + offset = sd->sub_hdr_offset + sizeof(uint32_t) + + sd->dump_header->nr_cpus * sizeof(struct sadump_apic_state) + + apicid * sizeof(scs); + + while (apicid < sh->nr_cpus) { + apicid++; + if (!read_device(&scs, sizeof(scs), &offset)) { + error(INFO, "sadump: cannot read sub header " + "cpu_state\n"); + return FALSE; + } + if (scs.Cr3 && (scs.IdtUpper || scs.IdtLower)) { + *smram = scs; + return TRUE; + } + } + + return FALSE; } int @@ -1695,7 +1698,8 @@ sadump_get_cr3_idtr(ulong *cr3, ulong *idtr) struct sadump_smram_cpu_state scs; memset(&scs, 0, sizeof(scs)); - get_sadump_smram_cpu_state_any(&scs); + if (!get_sadump_smram_cpu_state_any(&scs)) + return FALSE; *cr3 = scs.Cr3; *idtr = ((uint64_t)scs.IdtUpper)<<32 | (uint64_t)scs.IdtLower; -- 2.7.4