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 | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
kaslr_helper.c | 9 ++++++++-
symbols.c | 4 ++++
x86_64.c | 3 ++-
5 files changed, 79 insertions(+), 3 deletions(-)
diff --git a/defs.h b/defs.h
index ec2d61c..fe87a05 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)
@@ -6274,6 +6274,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);
@@ -6285,6 +6286,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..40ad59d 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,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 +289,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 +342,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 +773,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 +872,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 +987,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 +2466,34 @@ 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 d02ef72..8646b8f 100644
--- a/kaslr_helper.c
+++ b/kaslr_helper.c
@@ -241,7 +241,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 f6f6c0e..c36bf66 100644
--- a/x86_64.c
+++ b/x86_64.c
@@ -6633,7 +6633,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