[Crash-utility] [PATCH v2 4/4] kaslr: fix failure of calculating kaslr_offset due to an sadump format restriction

HATAYAMA Daisuke d.hatayama at fujitsu.com
Mon Jul 27 10:00:23 UTC 2020


From: HATAYAMA Daisuke <d.hatayama at jp.fujitsu.com>

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.

Signed-off-by: HATAYAMA Daisuke <d.hatayama at fujitsu.com>
---
 kaslr_helper.c | 39 +++++++++++++++++++++++++++++++++++----
 sadump.c       | 20 ++++++++++++--------
 2 files changed, 47 insertions(+), 12 deletions(-)

diff --git a/kaslr_helper.c b/kaslr_helper.c
index acbb5c2..bb19e54 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 35f7cf0..26ed119 100644
--- a/sadump.c
+++ b/sadump.c
@@ -1666,21 +1666,24 @@ 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;
+        static int apicid;
+        struct sadump_smram_cpu_state scs;
 
-        offset = sd->sub_hdr_offset + sizeof(uint32_t) +
-                 sd->dump_header->nr_cpus * sizeof(struct sadump_apic_state);
+        if (apicid >= sh->nr_cpus)
+                 return FALSE;
 
-        memset(&zero, 0, sizeof(zero));
+        offset = sd->sub_hdr_offset + sizeof(uint32_t) +
+                 sd->dump_header->nr_cpus * sizeof(struct sadump_apic_state) +
+                 apicid * sizeof(scs);
 
-        for (apicid = 0; apicid < sh->nr_cpus; ++apicid) {
+        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 (memcmp(&scs, &zero, sizeof(scs)) != 0) {
+                if (scs.Cr3 && (scs.IdtUpper || scs.IdtLower)) {
                         *smram = scs;
                         return TRUE;
                 }
@@ -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;
-- 
1.8.3.1




More information about the Crash-utility mailing list