[Crash-utility] [PATCH v4 4/6] diskdump: infer kaslr offset for QEMU COMPRESSED dumps without vmcoreinfo

Sergio Lopez slp at redhat.com
Tue Mar 27 10:09:50 UTC 2018


If a QEMU COMPRESSED dump from a KASLR-enabled kernel is missing the
vmcoreinfo data, try to calculate phys_base and kaslr_offset by using
the technique developed by Takao Indoh.
---
 defs.h         |  5 ++++-
 diskdump.c     | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 kaslr_helper.c |  9 +++++++-
 symbols.c      |  4 ++++
 x86_64.c       |  3 ++-
 5 files changed, 83 insertions(+), 4 deletions(-)

diff --git a/defs.h b/defs.h
index b117c4a..ba2e5e1 100644
--- a/defs.h
+++ b/defs.h
@@ -285,7 +285,7 @@ struct number_option {
 #define SADUMP_DUMPFILE()  (pc->flags & SADUMP)
 #define VMSS_DUMPFILE()     (pc->flags & VMWARE_VMSS)
 #define QEMU_MEM_DUMP_NO_VMCOREINFO() \
-	    ((pc->flags2 & (QEMU_MEM_DUMP_ELF)) && !(pc->flags2 & VMCOREINFO))
+	    ((pc->flags2 & (QEMU_MEM_DUMP_ELF|QEMU_MEM_DUMP_COMPRESSED)) && !(pc->flags2 & VMCOREINFO))
 
 
 #define NETDUMP_LOCAL    (0x1)  /* netdump_data flags */
@@ -6276,6 +6276,7 @@ int diskdump_memory_dump(FILE *);
 FILE *set_diskdump_fp(FILE *);
 void get_diskdump_regs(struct bt_info *, ulong *, ulong *);
 int diskdump_phys_base(unsigned long *);
+int diskdump_set_phys_base(unsigned long);
 ulong *diskdump_flags;
 int is_partial_diskdump(void);
 int dumpfile_is_split(void);
@@ -6287,6 +6288,8 @@ void diskdump_display_regs(int, FILE *);
 void process_elf32_notes(void *, ulong);
 void process_elf64_notes(void *, ulong);
 void dump_registers_for_compressed_kdump(void);
+int diskdump_kaslr_check(void);
+QEMUCPUState *diskdump_get_qemucpustate(int);
 
 /*
  * makedumpfile.c
diff --git a/diskdump.c b/diskdump.c
index b08a46c..8085712 100644
--- a/diskdump.c
+++ b/diskdump.c
@@ -56,6 +56,7 @@ struct diskdump_data {
 	void	**nt_prstatus_percpu;
 	uint	num_prstatus_notes;
 	void	**nt_qemu_percpu;
+	void	**nt_qemucs_percpu;
 	uint	num_qemu_notes;
 
 	/* page cache */
@@ -153,8 +154,14 @@ resize_note_pointers:
 		    	    dd->num_qemu_notes * sizeof(void *))) == NULL)
 				error(FATAL, 
 				    "compressed kdump: cannot realloc QEMU note pointers\n");
-		} else
+			if  ((dd->nt_qemucs_percpu = realloc(dd->nt_qemucs_percpu,
+			    dd->num_qemu_notes * sizeof(void *))) == NULL)
+				error(FATAL,
+				    "compressed kdump: cannot realloc QEMU note pointers\n");
+		} else {
 			free(dd->nt_qemu_percpu);
+			free(dd->nt_qemucs_percpu);
+		}
 	}
 }
 
@@ -283,6 +290,10 @@ process_elf32_notes(void *note_buf, unsigned long size_note)
 		}
 		len = sizeof(Elf32_Nhdr);
 		if (STRNEQ((char *)nt + len, "QEMU")) {
+			ulong *ptr =
+			    (ulong *)((char *)nt + sizeof(Elf32_Nhdr) + nt->n_namesz);
+			dd->nt_qemucs_percpu[qemu_num] =
+			    (ulong *)roundup((ulong) ptr, 4);
 			dd->nt_qemu_percpu[qemu_num] = nt;
 			qemu_num++;
 		}
@@ -332,6 +343,10 @@ process_elf64_notes(void *note_buf, unsigned long size_note)
 		}
 		len = sizeof(Elf64_Nhdr);
 		if (STRNEQ((char *)nt + len, "QEMU")) {
+			ulong *ptr =
+			    (ulong *)((char *)nt + sizeof(Elf64_Nhdr) + nt->n_namesz);
+			dd->nt_qemucs_percpu[qemu_num] =
+			    (ulong *)roundup((ulong) ptr, 4);
 			dd->nt_qemu_percpu[qemu_num] = nt;
 			qemu_num++;
 		}
@@ -759,6 +774,10 @@ restart:
 			error(FATAL, "qemu mem dump compressed: cannot malloc pointer"
 				" to QEMU notes\n");
 
+		if ((dd->nt_qemucs_percpu = malloc(NR_CPUS * sizeof(void *))) == NULL)
+			error(FATAL, "qemu mem dump compressed: cannot malloc pointer"
+				" to QEMUCS notes\n");
+
 		if (FLAT_FORMAT()) {
 			if (!read_flattened_format(dd->dfd, offset, dd->notes_buf, size)) {
 				error(INFO, "compressed kdump: cannot read notes data"
@@ -854,6 +873,8 @@ err:
 		free(dd->nt_prstatus_percpu);
 	if (dd->nt_qemu_percpu)
 		free(dd->nt_qemu_percpu);
+	if (dd->nt_qemucs_percpu)
+		free(dd->nt_qemucs_percpu);
 
 	dd->flags &= ~(DISKDUMP_LOCAL|KDUMP_CMPRS_LOCAL);
 	pc->flags2 &= ~ELF_NOTES;
@@ -967,6 +988,17 @@ diskdump_phys_base(unsigned long *phys_base)
 	return FALSE;
 }
 
+int
+diskdump_set_phys_base(unsigned long phys_base)
+{
+	if (diskdump_kaslr_check()) {
+		dd->sub_header_kdump->phys_base = phys_base;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
 /*
  *  Check whether paddr is already cached.
  */
@@ -2435,4 +2467,36 @@ dump_registers_for_compressed_kdump(void)
 	}
 }
 
+int
+diskdump_kaslr_check()
+{
+	if (!QEMU_MEM_DUMP_NO_VMCOREINFO())
+		return FALSE;
+
+	if (dd->num_qemu_notes)
+		return TRUE;
+
+	return FALSE;
+}
 
+#ifdef X86_64
+QEMUCPUState *
+diskdump_get_qemucpustate(int cpu)
+{
+        if (cpu >= dd->num_qemu_notes) {
+                if (CRASHDEBUG(1))
+                        error(INFO,
+                            "Invalid index for QEMU Note: %d (>= %d)\n",
+                            cpu, dd->num_qemu_notes);
+                return NULL;
+        }
+
+        if (dd->machine_type != EM_X86_64) {
+                if (CRASHDEBUG(1))
+                        error(INFO, "Only x86_64 64bit is supported.\n");
+                return NULL;
+        }
+
+        return (QEMUCPUState *)dd->nt_qemucs_percpu[cpu];
+}
+#endif
diff --git a/kaslr_helper.c b/kaslr_helper.c
index 9f04e3b..168cfb9 100644
--- a/kaslr_helper.c
+++ b/kaslr_helper.c
@@ -242,7 +242,14 @@ qemu_get_cr3_idtr(ulong *cr3, ulong *idtr)
 {
 	QEMUCPUState *cpustat;
 
-	cpustat = kdump_get_qemucpustate(0);
+	if (DISKDUMP_DUMPFILE()) {
+		cpustat = diskdump_get_qemucpustate(0);
+	} else if (KDUMP_DUMPFILE()) {
+		cpustat = kdump_get_qemucpustate(0);
+	} else {
+		return FALSE;
+	}
+
 	if (!cpustat) {
 		return FALSE;
 	}
diff --git a/symbols.c b/symbols.c
index 348d9ae..ddbce7d 100644
--- a/symbols.c
+++ b/symbols.c
@@ -613,6 +613,8 @@ kaslr_init(void)
 	if (QEMU_MEM_DUMP_NO_VMCOREINFO()) {
 		if (KDUMP_DUMPFILE() && kdump_kaslr_check()) {
 			kt->flags2 |= KASLR_CHECK;
+		} else if (DISKDUMP_DUMPFILE() && diskdump_kaslr_check()) {
+			kt->flags2 |= KASLR_CHECK;
 		}
 	} else if (KDUMP_DUMPFILE() || DISKDUMP_DUMPFILE()) {
 		if ((string = pc->read_vmcoreinfo("SYMBOL(_stext)"))) {
@@ -660,6 +662,8 @@ derive_kaslr_offset(bfd *abfd, int dynamic, bfd_byte *start, bfd_byte *end,
 				sadump_set_phys_base(phys_base);
 			else if (KDUMP_DUMPFILE())
 				kdump_set_phys_base(phys_base);
+			else if (DISKDUMP_DUMPFILE())
+				diskdump_set_phys_base(phys_base);
 		}
 
 		return;
diff --git a/x86_64.c b/x86_64.c
index 4affc67..e46e70f 100644
--- a/x86_64.c
+++ b/x86_64.c
@@ -6642,7 +6642,8 @@ x86_64_calc_phys_base(void)
 	 */
 
 	if (QEMU_MEM_DUMP_NO_VMCOREINFO()) {
-		if (KDUMP_DUMPFILE() && kdump_phys_base(&phys_base))
+		if ((KDUMP_DUMPFILE() && kdump_phys_base(&phys_base)) ||
+		    (DISKDUMP_DUMPFILE() && diskdump_phys_base(&phys_base)))
 			machdep->machspec->phys_base = phys_base;
 
 		if (!x86_64_virt_phys_base())
-- 
2.14.3




More information about the Crash-utility mailing list