Kernel analysis utility for live systems, netdump, diskdump, kdump, LKCD or mcore dumpfiles
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

368 lines
12 KiB

From b97e7fd4e8268d5c46f1b30b41ce1f6ca9ceb216 Mon Sep 17 00:00:00 2001
From: HATAYAMA Daisuke <d.hatayama@fujitsu.com>
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 <d.hatayama@fujitsu.com>
---
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 <d.hatayama@fujitsu.com>
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 <d.hatayama@fujitsu.com>
---
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 <d.hatayama@fujitsu.com>
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 <d.hatayama@fujitsu.com>
---
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 <d.hatayama@fujitsu.com>
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 <d.hatayama@fujitsu.com>
---
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