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