|
|
1d9674 |
From e1ac694b94ebfa7204c5b1fac1a87d204b48f5b4 Mon Sep 17 00:00:00 2001
|
|
|
1d9674 |
From: Takao Indoh <indou.takao@jp.fujitsu.com>
|
|
|
1d9674 |
Date: Thu, 26 Oct 2017 20:32:54 +0900
|
|
|
1d9674 |
Subject: [PATCH 3/4] [PATCH v3 3/4] sadump: Fix a KASLR problem of sadump
|
|
|
1d9674 |
|
|
|
1d9674 |
This patch fix a problem that makedumpfile cannot handle a dumpfile
|
|
|
1d9674 |
which is captured by sadump in KASLR enabled kernel.
|
|
|
1d9674 |
|
|
|
1d9674 |
When KASLR feature is enabled, a kernel is placed on the memory randomly
|
|
|
1d9674 |
and therefore makedumpfile cannot handle a dumpfile captured by sadump
|
|
|
1d9674 |
because addresses of kernel symbols in System.map or vmlinux are
|
|
|
1d9674 |
different from actual addresses.
|
|
|
1d9674 |
|
|
|
1d9674 |
To solve this problem, we need to calculate kaslr offset(the difference
|
|
|
1d9674 |
between original symbol address and actual address) and phys_base, and
|
|
|
1d9674 |
adjust symbol table of makedumpfile. In the case of dumpfile of kdump,
|
|
|
1d9674 |
these information is included in the header, but dumpfile of sadump does
|
|
|
1d9674 |
not have such a information.
|
|
|
1d9674 |
|
|
|
1d9674 |
This patch calculate kaslr offset and phys_base to solve this problem.
|
|
|
1d9674 |
Please see the comment in the calc_kaslr_offset() for the detail idea.
|
|
|
1d9674 |
The basic idea is getting register (IDTR and CR3) from dump header, and
|
|
|
1d9674 |
calculate kaslr_offset/phys_base using them.
|
|
|
1d9674 |
|
|
|
1d9674 |
Signed-off-by: Takao Indoh <indou.takao@jp.fujitsu.com>
|
|
|
1d9674 |
---
|
|
|
1d9674 |
makedumpfile.c | 10 ++++
|
|
|
1d9674 |
makedumpfile.h | 5 +-
|
|
|
1d9674 |
sadump_info.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
|
|
|
1d9674 |
3 files changed, 155 insertions(+), 3 deletions(-)
|
|
|
1d9674 |
|
|
|
1d9674 |
diff --git a/makedumpfile.c b/makedumpfile.c
|
|
|
1d9674 |
index 5f2ca7d0fbc8..41438a344574 100644
|
|
|
1d9674 |
--- a/makedumpfile-1.6.2/makedumpfile.c
|
|
|
1d9674 |
+++ b/makedumpfile-1.6.2/makedumpfile.c
|
|
|
1d9674 |
@@ -1554,6 +1554,9 @@ get_symbol_info(void)
|
|
|
1d9674 |
SYMBOL_INIT(demote_segment_4k, "demote_segment_4k");
|
|
|
1d9674 |
SYMBOL_INIT(cur_cpu_spec, "cur_cpu_spec");
|
|
|
1d9674 |
|
|
|
1d9674 |
+ SYMBOL_INIT(divide_error, "divide_error");
|
|
|
1d9674 |
+ SYMBOL_INIT(idt_table, "idt_table");
|
|
|
1d9674 |
+
|
|
|
1d9674 |
return TRUE;
|
|
|
1d9674 |
}
|
|
|
1d9674 |
|
|
|
1d9674 |
@@ -2249,6 +2252,13 @@ write_vmcoreinfo_data(void)
|
|
|
1d9674 |
WRITE_NUMBER_UNSIGNED("kimage_voffset", kimage_voffset);
|
|
|
1d9674 |
#endif
|
|
|
1d9674 |
|
|
|
1d9674 |
+ if (info->phys_base)
|
|
|
1d9674 |
+ fprintf(info->file_vmcoreinfo, "%s%lu\n", STR_NUMBER("phys_base"),
|
|
|
1d9674 |
+ info->phys_base);
|
|
|
1d9674 |
+ if (info->kaslr_offset)
|
|
|
1d9674 |
+ fprintf(info->file_vmcoreinfo, "%s%lx\n", STR_KERNELOFFSET,
|
|
|
1d9674 |
+ info->kaslr_offset);
|
|
|
1d9674 |
+
|
|
|
1d9674 |
/*
|
|
|
1d9674 |
* write the source file of 1st kernel
|
|
|
1d9674 |
*/
|
|
|
1d9674 |
diff --git a/makedumpfile.h b/makedumpfile.h
|
|
|
1d9674 |
index f48dc0b82d4a..5f814e7bc3c1 100644
|
|
|
1d9674 |
--- a/makedumpfile-1.6.2/makedumpfile.h
|
|
|
1d9674 |
+++ b/makedumpfile-1.6.2/makedumpfile.h
|
|
|
1d9674 |
@@ -45,6 +45,7 @@
|
|
|
1d9674 |
#include "sadump_mod.h"
|
|
|
1d9674 |
#include <pthread.h>
|
|
|
1d9674 |
#include <semaphore.h>
|
|
|
1d9674 |
+#include <inttypes.h>
|
|
|
1d9674 |
|
|
|
1d9674 |
#define VMEMMAPSTART 0xffffea0000000000UL
|
|
|
1d9674 |
#define BITS_PER_WORD 64
|
|
|
1d9674 |
@@ -1599,6 +1600,8 @@ struct symbol_table {
|
|
|
1d9674 |
unsigned long long cpu_online_mask;
|
|
|
1d9674 |
unsigned long long __cpu_online_mask;
|
|
|
1d9674 |
unsigned long long kexec_crash_image;
|
|
|
1d9674 |
+ unsigned long long divide_error;
|
|
|
1d9674 |
+ unsigned long long idt_table;
|
|
|
1d9674 |
|
|
|
1d9674 |
/*
|
|
|
1d9674 |
* symbols on ppc64 arch
|
|
|
1d9674 |
@@ -1960,7 +1963,7 @@ int iomem_for_each_line(char *match, int (*callback)(void *data, int nr,
|
|
|
1d9674 |
unsigned long length),
|
|
|
1d9674 |
void *data);
|
|
|
1d9674 |
int is_bigendian(void);
|
|
|
1d9674 |
-
|
|
|
1d9674 |
+int get_symbol_info(void);
|
|
|
1d9674 |
|
|
|
1d9674 |
/*
|
|
|
1d9674 |
* for Xen extraction
|
|
|
1d9674 |
diff --git a/sadump_info.c b/sadump_info.c
|
|
|
1d9674 |
index 7dd22e704234..29ccef881370 100644
|
|
|
1d9674 |
--- a/makedumpfile-1.6.2/sadump_info.c
|
|
|
1d9674 |
+++ b/makedumpfile-1.6.2/sadump_info.c
|
|
|
1d9674 |
@@ -1035,6 +1035,138 @@ sadump_get_max_mapnr(void)
|
|
|
1d9674 |
|
|
|
1d9674 |
#ifdef __x86_64__
|
|
|
1d9674 |
|
|
|
1d9674 |
+/*
|
|
|
1d9674 |
+ * Get address of vector0 interrupt handler (Devide Error) form Interrupt
|
|
|
1d9674 |
+ * Descriptor Table.
|
|
|
1d9674 |
+ */
|
|
|
1d9674 |
+static unsigned long
|
|
|
1d9674 |
+get_vec0_addr(ulong idtr)
|
|
|
1d9674 |
+{
|
|
|
1d9674 |
+ struct gate_struct64 {
|
|
|
1d9674 |
+ uint16_t offset_low;
|
|
|
1d9674 |
+ uint16_t segment;
|
|
|
1d9674 |
+ uint32_t ist : 3, zero0 : 5, type : 5, dpl : 2, p : 1;
|
|
|
1d9674 |
+ uint16_t offset_middle;
|
|
|
1d9674 |
+ uint32_t offset_high;
|
|
|
1d9674 |
+ uint32_t zero1;
|
|
|
1d9674 |
+ } __attribute__((packed)) gate;
|
|
|
1d9674 |
+
|
|
|
1d9674 |
+ readmem(PADDR, idtr, &gate, sizeof(gate));
|
|
|
1d9674 |
+
|
|
|
1d9674 |
+ return ((ulong)gate.offset_high << 32)
|
|
|
1d9674 |
+ + ((ulong)gate.offset_middle << 16)
|
|
|
1d9674 |
+ + gate.offset_low;
|
|
|
1d9674 |
+}
|
|
|
1d9674 |
+
|
|
|
1d9674 |
+/*
|
|
|
1d9674 |
+ * Calculate kaslr_offset and phys_base
|
|
|
1d9674 |
+ *
|
|
|
1d9674 |
+ * kaslr_offset:
|
|
|
1d9674 |
+ * The difference between original address in vmlinux and actual address
|
|
|
1d9674 |
+ * placed randomly by kaslr feature. To be more accurate,
|
|
|
1d9674 |
+ * kaslr_offset = actual address - original address
|
|
|
1d9674 |
+ *
|
|
|
1d9674 |
+ * phys_base:
|
|
|
1d9674 |
+ * Physical address where the kerenel is placed. In other words, it's a
|
|
|
1d9674 |
+ * physical address of __START_KERNEL_map. This is also decided randomly by
|
|
|
1d9674 |
+ * kaslr.
|
|
|
1d9674 |
+ *
|
|
|
1d9674 |
+ * kaslr offset and phys_base are calculated as follows:
|
|
|
1d9674 |
+ *
|
|
|
1d9674 |
+ * kaslr_offset:
|
|
|
1d9674 |
+ * 1) Get IDTR and CR3 value from the dump header.
|
|
|
1d9674 |
+ * 2) Get a virtual address of IDT from IDTR value
|
|
|
1d9674 |
+ * --- (A)
|
|
|
1d9674 |
+ * 3) Translate (A) to physical address using CR3, which points a top of
|
|
|
1d9674 |
+ * page table.
|
|
|
1d9674 |
+ * --- (B)
|
|
|
1d9674 |
+ * 4) Get an address of vector0 (Devide Error) interrupt handler from
|
|
|
1d9674 |
+ * IDT, which are pointed by (B).
|
|
|
1d9674 |
+ * --- (C)
|
|
|
1d9674 |
+ * 5) Get an address of symbol "divide_error" form vmlinux
|
|
|
1d9674 |
+ * --- (D)
|
|
|
1d9674 |
+ *
|
|
|
1d9674 |
+ * Now we have two addresses:
|
|
|
1d9674 |
+ * (C)-> Actual address of "divide_error"
|
|
|
1d9674 |
+ * (D)-> Original address of "divide_error" in the vmlinux
|
|
|
1d9674 |
+ *
|
|
|
1d9674 |
+ * kaslr_offset can be calculated by the difference between these two
|
|
|
1d9674 |
+ * value.
|
|
|
1d9674 |
+ *
|
|
|
1d9674 |
+ * phys_base;
|
|
|
1d9674 |
+ * 1) Get IDT virtual address from vmlinux
|
|
|
1d9674 |
+ * --- (E)
|
|
|
1d9674 |
+ *
|
|
|
1d9674 |
+ * So phys_base can be calculated using relationship of directly mapped
|
|
|
1d9674 |
+ * address.
|
|
|
1d9674 |
+ *
|
|
|
1d9674 |
+ * phys_base =
|
|
|
1d9674 |
+ * Physical address(B) -
|
|
|
1d9674 |
+ * (Virtual address(E) + kaslr_offset - __START_KERNEL_map)
|
|
|
1d9674 |
+ *
|
|
|
1d9674 |
+ * Note that the address (A) cannot be used instead of (E) because (A) is
|
|
|
1d9674 |
+ * not direct map address, it's a fixed map address.
|
|
|
1d9674 |
+ */
|
|
|
1d9674 |
+int
|
|
|
1d9674 |
+calc_kaslr_offset(void)
|
|
|
1d9674 |
+{
|
|
|
1d9674 |
+ struct sadump_header *sh = si->sh_memory;
|
|
|
1d9674 |
+ uint64_t idtr = 0, cr3 = 0, idtr_paddr;
|
|
|
1d9674 |
+ struct sadump_smram_cpu_state smram, zero;
|
|
|
1d9674 |
+ int apicid;
|
|
|
1d9674 |
+ unsigned long divide_error_vmcore, divide_error_vmlinux;
|
|
|
1d9674 |
+ unsigned long kaslr_offset, phys_base;
|
|
|
1d9674 |
+
|
|
|
1d9674 |
+ memset(&zero, 0, sizeof(zero));
|
|
|
1d9674 |
+ for (apicid = 0; apicid < sh->nr_cpus; ++apicid) {
|
|
|
1d9674 |
+ if (!get_smram_cpu_state(apicid, &smram)) {
|
|
|
1d9674 |
+ ERRMSG("get_smram_cpu_state error\n");
|
|
|
1d9674 |
+ return FALSE;
|
|
|
1d9674 |
+ }
|
|
|
1d9674 |
+
|
|
|
1d9674 |
+ if (memcmp(&smram, &zero, sizeof(smram)) != 0)
|
|
|
1d9674 |
+ break;
|
|
|
1d9674 |
+ }
|
|
|
1d9674 |
+ if (apicid >= sh->nr_cpus) {
|
|
|
1d9674 |
+ ERRMSG("Can't get smram state\n");
|
|
|
1d9674 |
+ return FALSE;
|
|
|
1d9674 |
+ }
|
|
|
1d9674 |
+
|
|
|
1d9674 |
+ idtr = ((uint64_t)smram.IdtUpper)<<32 | (uint64_t)smram.IdtLower;
|
|
|
1d9674 |
+ cr3 = smram.Cr3;
|
|
|
1d9674 |
+
|
|
|
1d9674 |
+ /* Convert virtual address of IDT table to physical address */
|
|
|
1d9674 |
+ if ((idtr_paddr = vtop4_x86_64_pagetable(idtr, cr3)) == NOT_PADDR)
|
|
|
1d9674 |
+ return FALSE;
|
|
|
1d9674 |
+
|
|
|
1d9674 |
+ /* Now we can calculate kaslr_offset and phys_base */
|
|
|
1d9674 |
+ divide_error_vmlinux = SYMBOL(divide_error);
|
|
|
1d9674 |
+ divide_error_vmcore = get_vec0_addr(idtr_paddr);
|
|
|
1d9674 |
+ kaslr_offset = divide_error_vmcore - divide_error_vmlinux;
|
|
|
1d9674 |
+ phys_base = idtr_paddr -
|
|
|
1d9674 |
+ (SYMBOL(idt_table) + kaslr_offset - __START_KERNEL_map);
|
|
|
1d9674 |
+
|
|
|
1d9674 |
+ info->kaslr_offset = kaslr_offset;
|
|
|
1d9674 |
+ info->phys_base = phys_base;
|
|
|
1d9674 |
+
|
|
|
1d9674 |
+ DEBUG_MSG("sadump: idtr=%" PRIx64 "\n", idtr);
|
|
|
1d9674 |
+ DEBUG_MSG("sadump: cr3=%" PRIx64 "\n", cr3);
|
|
|
1d9674 |
+ DEBUG_MSG("sadump: idtr(phys)=%" PRIx64 "\n", idtr_paddr);
|
|
|
1d9674 |
+ DEBUG_MSG("sadump: devide_error(vmlinux)=%lx\n",
|
|
|
1d9674 |
+ divide_error_vmlinux);
|
|
|
1d9674 |
+ DEBUG_MSG("sadump: devide_error(vmcore)=%lx\n",
|
|
|
1d9674 |
+ divide_error_vmcore);
|
|
|
1d9674 |
+
|
|
|
1d9674 |
+ /* Reload symbol */
|
|
|
1d9674 |
+ if (!get_symbol_info())
|
|
|
1d9674 |
+ return FALSE;
|
|
|
1d9674 |
+
|
|
|
1d9674 |
+ DEBUG_MSG("sadump: kaslr_offset=%lx\n", info->kaslr_offset);
|
|
|
1d9674 |
+ DEBUG_MSG("sadump: phys_base=%lx\n", info->phys_base);
|
|
|
1d9674 |
+
|
|
|
1d9674 |
+ return TRUE;
|
|
|
1d9674 |
+}
|
|
|
1d9674 |
+
|
|
|
1d9674 |
int
|
|
|
1d9674 |
sadump_virt_phys_base(void)
|
|
|
1d9674 |
{
|
|
|
1d9674 |
@@ -1065,6 +1197,9 @@ sadump_virt_phys_base(void)
|
|
|
1d9674 |
}
|
|
|
1d9674 |
|
|
|
1d9674 |
failed:
|
|
|
1d9674 |
+ if (calc_kaslr_offset())
|
|
|
1d9674 |
+ return TRUE;
|
|
|
1d9674 |
+
|
|
|
1d9674 |
info->phys_base = 0;
|
|
|
1d9674 |
|
|
|
1d9674 |
DEBUG_MSG("sadump: failed to calculate phys_base; default to 0\n");
|
|
|
1d9674 |
@@ -1518,10 +1653,14 @@ cpu_to_apicid(int cpu, int *apicid)
|
|
|
1d9674 |
if (!readmem(VADDR, SYMBOL(x86_bios_cpu_apicid_early_ptr),
|
|
|
1d9674 |
&early_ptr, sizeof(early_ptr)))
|
|
|
1d9674 |
return FALSE;
|
|
|
1d9674 |
-
|
|
|
1d9674 |
+ /*
|
|
|
1d9674 |
+ * Note: SYMBOL(name) value is adjusted by info->kaslr_offset,
|
|
|
1d9674 |
+ * but per_cpu symbol does not need to be adjusted becasue it
|
|
|
1d9674 |
+ * is not affected by kaslr.
|
|
|
1d9674 |
+ */
|
|
|
1d9674 |
apicid_addr = early_ptr
|
|
|
1d9674 |
? SYMBOL(x86_bios_cpu_apicid_early_map)+cpu*sizeof(uint16_t)
|
|
|
1d9674 |
- : per_cpu_ptr(SYMBOL(x86_bios_cpu_apicid), cpu);
|
|
|
1d9674 |
+ : per_cpu_ptr(SYMBOL(x86_bios_cpu_apicid) - info->kaslr_offset, cpu);
|
|
|
1d9674 |
|
|
|
1d9674 |
if (!readmem(VADDR, apicid_addr, &apicid_u16, sizeof(uint16_t)))
|
|
|
1d9674 |
return FALSE;
|
|
|
1d9674 |
--
|
|
|
1d9674 |
2.5.5
|
|
|
1d9674 |
|