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.
263 lines
8.0 KiB
263 lines
8.0 KiB
6 years ago
|
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
|
||
|
|