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

Sergio Lopez slp at redhat.com
Fri Mar 16 17:31:07 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         |  7 ++++-
 diskdump.c     | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 kaslr_helper.c |  3 ++
 symbols.c      |  4 +++
 x86_64.c       | 11 +++++--
 5 files changed, 118 insertions(+), 3 deletions(-)

diff --git a/defs.h b/defs.h
index 84fc85c..8c60b75 100644
--- a/defs.h
+++ b/defs.h
@@ -284,7 +284,7 @@ struct number_option {
 #define KVMDUMP_DUMPFILE()  (pc->flags & KVMDUMP)
 #define SADUMP_DUMPFILE()  (pc->flags & SADUMP)
 #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 */
 #define NETDUMP_REMOTE   (0x2)  
@@ -6286,6 +6286,11 @@ 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);
+int diskdump_kaslr_phys_base(ulong *);
+int diskdump_set_kaslr_phys_base(ulong);
+ulong diskdump_get_idtr(void);
+ulong diskdump_get_cr3(void);
 
 /*
  * makedumpfile.c
diff --git a/diskdump.c b/diskdump.c
index b08a46c..1ec4bcf 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 */
@@ -72,6 +73,7 @@ struct diskdump_data {
 	ulong  *valid_pages;
 	ulong   accesses;
 	ulong	snapshot_task;
+	ulong	kaslr_phys_base;
 };
 
 static struct diskdump_data diskdump_data = { 0 };
@@ -153,8 +155,13 @@ resize_note_pointers:
 		    	    dd->num_qemu_notes * sizeof(void *))) == NULL)
 				error(FATAL, 
 				    "compressed kdump: cannot realloc QEMU note pointers\n");
+			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;
@@ -2435,4 +2456,79 @@ 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;
+}
+
+int diskdump_kaslr_phys_base(ulong *kaslr_phys_base)
+{
+	if (!diskdump_kaslr_check())
+		return FALSE;
+
+	if (dd->kaslr_phys_base) {
+		*kaslr_phys_base = dd->kaslr_phys_base;
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+int diskdump_set_kaslr_phys_base(ulong kaslr_phys_base)
+{
+	if (!diskdump_kaslr_check())
+		return FALSE;
+
+	dd->kaslr_phys_base = kaslr_phys_base;
 
+	return TRUE;
+}
+
+#ifdef X86_64
+static QEMUCPUState * 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];
+}
+
+ulong diskdump_get_idtr()
+{
+	QEMUCPUState *cpustat;
+
+	cpustat = get_qemucpustate(0);
+	if (!cpustat) {
+		return 0;
+	}
+	return cpustat->idt.base;
+}
+
+ulong diskdump_get_cr3()
+{
+	QEMUCPUState *cpustat;
+
+	cpustat = get_qemucpustate(0);
+	if (!cpustat) {
+		return 0;
+	}
+	return cpustat->cr[3];
+}
+#endif
diff --git a/kaslr_helper.c b/kaslr_helper.c
index 1079863..5b71e3e 100644
--- a/kaslr_helper.c
+++ b/kaslr_helper.c
@@ -386,6 +386,9 @@ calc_kaslr_offset(ulong *kaslr_offset, ulong *phys_base)
 		if (KDUMP_DUMPFILE()) {
 			idtr = kdump_get_idtr();
 			cr3 = kdump_get_cr3();
+		} else if (DISKDUMP_DUMPFILE()) {
+			idtr = diskdump_get_idtr();
+			cr3 = diskdump_get_cr3();
 		} else {
 			return FALSE;
 		}
diff --git a/symbols.c b/symbols.c
index 348d9ae..614a36d 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_kaslr_phys_base(phys_base);
 		}
 
 		return;
diff --git a/x86_64.c b/x86_64.c
index ed5985a..3c492e4 100644
--- a/x86_64.c
+++ b/x86_64.c
@@ -6632,8 +6632,15 @@ x86_64_calc_phys_base(void)
 	 *  Get relocation value from whatever dumpfile format is being used.
 	 */
 
-	if (QEMU_MEM_DUMP_NO_VMCOREINFO() && KDUMP_DUMPFILE()) {
-		if (kdump_phys_base(&phys_base)) {
+	if (QEMU_MEM_DUMP_NO_VMCOREINFO()) {
+		int ret;
+
+		if (KDUMP_DUMPFILE())
+			ret = kdump_phys_base(&phys_base);
+		else if (DISKDUMP_DUMPFILE())
+			ret = diskdump_kaslr_phys_base(&phys_base);
+
+		if (ret) {
 			machdep->machspec->phys_base = phys_base;
 			if (CRASHDEBUG(1))
 				fprintf(fp, "kdump-novmci: phys base: %lx\n",
-- 
2.14.3




More information about the Crash-utility mailing list