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.
360 lines
9.8 KiB
360 lines
9.8 KiB
6 years ago
|
From 09288f2058c8066a2729588ec668eed3e1140028 Mon Sep 17 00:00:00 2001
|
||
|
From: Takao Indoh <indou.takao@jp.fujitsu.com>
|
||
|
Date: Thu, 26 Oct 2017 20:33:01 +0900
|
||
|
Subject: [PATCH 4/4] [PATCH v3 4/4] sadump: Fix a KASLR problem of sadump
|
||
|
while kdump is working
|
||
|
|
||
|
In the calc_kaslr_offset(), kaslr_offset and phys_base are calculated
|
||
|
using IDTR and CR3, but this solution does not work in the following
|
||
|
cases.
|
||
|
|
||
|
1) If the dump is captured on early stage of kernel boot, IDTR points
|
||
|
early IDT table(early_idts) instead of normal IDT(idt_table).
|
||
|
2) If the dump is captured whle kdump is working, IDTR points IDT table
|
||
|
of 2nd kernel, not 1st kernel.
|
||
|
|
||
|
This patch fixes the case 2). Current implementation does not support
|
||
|
the case 1), need enhancement in the future. This patch gets kernel boot
|
||
|
parameter from "saved_command_line" and check if "elfcorehdr=" is
|
||
|
included in the parameter. If it's included, we are in the 2nd kernel.
|
||
|
Retrieve vmcoreinfo from address of "elfcorehdr=" and get kaslr_offset
|
||
|
and phys_base from vmcoreinfo.
|
||
|
|
||
|
Signed-off-by: Takao Indoh <indou.takao@jp.fujitsu.com>
|
||
|
---
|
||
|
makedumpfile.c | 1 +
|
||
|
makedumpfile.h | 1 +
|
||
|
sadump_info.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||
|
3 files changed, 273 insertions(+)
|
||
|
|
||
|
diff --git a/makedumpfile.c b/makedumpfile.c
|
||
|
index 41438a344574..6d5fc8b95415 100644
|
||
|
--- a/makedumpfile-1.6.2/makedumpfile.c
|
||
|
+++ b/makedumpfile-1.6.2/makedumpfile.c
|
||
|
@@ -1556,6 +1556,7 @@ get_symbol_info(void)
|
||
|
|
||
|
SYMBOL_INIT(divide_error, "divide_error");
|
||
|
SYMBOL_INIT(idt_table, "idt_table");
|
||
|
+ SYMBOL_INIT(saved_command_line, "saved_command_line");
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
diff --git a/makedumpfile.h b/makedumpfile.h
|
||
|
index 5f814e7bc3c1..db753792bca6 100644
|
||
|
--- a/makedumpfile-1.6.2/makedumpfile.h
|
||
|
+++ b/makedumpfile-1.6.2/makedumpfile.h
|
||
|
@@ -1602,6 +1602,7 @@ struct symbol_table {
|
||
|
unsigned long long kexec_crash_image;
|
||
|
unsigned long long divide_error;
|
||
|
unsigned long long idt_table;
|
||
|
+ unsigned long long saved_command_line;
|
||
|
|
||
|
/*
|
||
|
* symbols on ppc64 arch
|
||
|
diff --git a/sadump_info.c b/sadump_info.c
|
||
|
index 29ccef881370..148d4baaa538 100644
|
||
|
--- a/makedumpfile-1.6.2/sadump_info.c
|
||
|
+++ b/makedumpfile-1.6.2/sadump_info.c
|
||
|
@@ -1059,6 +1059,241 @@ get_vec0_addr(ulong idtr)
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
+ * Parse a string of [size[KMG]@]offset[KMG]
|
||
|
+ * Import from Linux kernel(lib/cmdline.c)
|
||
|
+ */
|
||
|
+static ulong memparse(char *ptr, char **retptr)
|
||
|
+{
|
||
|
+ char *endptr;
|
||
|
+
|
||
|
+ unsigned long long ret = strtoull(ptr, &endptr, 0);
|
||
|
+
|
||
|
+ switch (*endptr) {
|
||
|
+ case 'E':
|
||
|
+ case 'e':
|
||
|
+ ret <<= 10;
|
||
|
+ case 'P':
|
||
|
+ case 'p':
|
||
|
+ ret <<= 10;
|
||
|
+ case 'T':
|
||
|
+ case 't':
|
||
|
+ ret <<= 10;
|
||
|
+ case 'G':
|
||
|
+ case 'g':
|
||
|
+ ret <<= 10;
|
||
|
+ case 'M':
|
||
|
+ case 'm':
|
||
|
+ ret <<= 10;
|
||
|
+ case 'K':
|
||
|
+ case 'k':
|
||
|
+ ret <<= 10;
|
||
|
+ endptr++;
|
||
|
+ default:
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (retptr)
|
||
|
+ *retptr = endptr;
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Find "elfcorehdr=" in the boot parameter of kernel and return the address
|
||
|
+ * of elfcorehdr.
|
||
|
+ */
|
||
|
+static ulong
|
||
|
+get_elfcorehdr(ulong cr3)
|
||
|
+{
|
||
|
+ char cmdline[BUFSIZE], *ptr;
|
||
|
+ ulong cmdline_vaddr;
|
||
|
+ ulong cmdline_paddr;
|
||
|
+ ulong buf_vaddr, buf_paddr;
|
||
|
+ char *end;
|
||
|
+ ulong elfcorehdr_addr = 0, elfcorehdr_size = 0;
|
||
|
+
|
||
|
+ if (SYMBOL(saved_command_line) == NOT_FOUND_SYMBOL) {
|
||
|
+ ERRMSG("Can't get the symbol of saved_command_line.\n");
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+ cmdline_vaddr = SYMBOL(saved_command_line);
|
||
|
+ if ((cmdline_paddr = vtop4_x86_64_pagetable(cmdline_vaddr, cr3)) == NOT_PADDR)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ DEBUG_MSG("sadump: cmdline vaddr: %lx\n", cmdline_vaddr);
|
||
|
+ DEBUG_MSG("sadump: cmdline paddr: %lx\n", cmdline_paddr);
|
||
|
+
|
||
|
+ if (!readmem(PADDR, cmdline_paddr, &buf_vaddr, sizeof(ulong)))
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ if ((buf_paddr = vtop4_x86_64_pagetable(buf_vaddr, cr3)) == NOT_PADDR)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ DEBUG_MSG("sadump: cmdline buf vaddr: %lx\n", buf_vaddr);
|
||
|
+ DEBUG_MSG("sadump: cmdline buf paddr: %lx\n", buf_paddr);
|
||
|
+
|
||
|
+ memset(cmdline, 0, BUFSIZE);
|
||
|
+ if (!readmem(PADDR, buf_paddr, cmdline, BUFSIZE))
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ ptr = strstr(cmdline, "elfcorehdr=");
|
||
|
+ if (!ptr)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ DEBUG_MSG("sadump: 2nd kernel detected.\n");
|
||
|
+
|
||
|
+ ptr += strlen("elfcorehdr=");
|
||
|
+ elfcorehdr_addr = memparse(ptr, &end);
|
||
|
+ if (*end == '@') {
|
||
|
+ elfcorehdr_size = elfcorehdr_addr;
|
||
|
+ elfcorehdr_addr = memparse(end + 1, &end);
|
||
|
+ }
|
||
|
+
|
||
|
+ DEBUG_MSG("sadump: elfcorehdr_addr: %lx\n", elfcorehdr_addr);
|
||
|
+ DEBUG_MSG("sadump: elfcorehdr_size: %lx\n", elfcorehdr_size);
|
||
|
+
|
||
|
+ return elfcorehdr_addr;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Get vmcoreinfo from elfcorehdr.
|
||
|
+ * Some codes are imported from Linux kernel(fs/proc/vmcore.c)
|
||
|
+ */
|
||
|
+static int
|
||
|
+get_vmcoreinfo_in_kdump_kernel(ulong elfcorehdr, ulong *addr, int *len)
|
||
|
+{
|
||
|
+ unsigned char e_ident[EI_NIDENT];
|
||
|
+ Elf64_Ehdr ehdr;
|
||
|
+ Elf64_Phdr phdr;
|
||
|
+ Elf64_Nhdr nhdr;
|
||
|
+ ulong ptr;
|
||
|
+ ulong nhdr_offset = 0;
|
||
|
+ int i;
|
||
|
+
|
||
|
+ if (!readmem(PADDR, elfcorehdr, e_ident, EI_NIDENT))
|
||
|
+ return FALSE;
|
||
|
+
|
||
|
+ if (e_ident[EI_CLASS] != ELFCLASS64) {
|
||
|
+ ERRMSG("Only ELFCLASS64 is supportd\n");
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!readmem(PADDR, elfcorehdr, &ehdr, sizeof(ehdr)))
|
||
|
+ return FALSE;
|
||
|
+
|
||
|
+ /* Sanity Check */
|
||
|
+ if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0 ||
|
||
|
+ (ehdr.e_type != ET_CORE) ||
|
||
|
+ ehdr.e_ident[EI_CLASS] != ELFCLASS64 ||
|
||
|
+ ehdr.e_ident[EI_VERSION] != EV_CURRENT ||
|
||
|
+ ehdr.e_version != EV_CURRENT ||
|
||
|
+ ehdr.e_ehsize != sizeof(Elf64_Ehdr) ||
|
||
|
+ ehdr.e_phentsize != sizeof(Elf64_Phdr) ||
|
||
|
+ ehdr.e_phnum == 0) {
|
||
|
+ ERRMSG("Invalid elf header\n");
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+
|
||
|
+ ptr = elfcorehdr + ehdr.e_phoff;
|
||
|
+ for (i = 0; i < ehdr.e_phnum; i++) {
|
||
|
+ ulong offset;
|
||
|
+ char name[16];
|
||
|
+
|
||
|
+ if (!readmem(PADDR, ptr, &phdr, sizeof(phdr)))
|
||
|
+ return FALSE;
|
||
|
+
|
||
|
+ ptr += sizeof(phdr);
|
||
|
+ if (phdr.p_type != PT_NOTE)
|
||
|
+ continue;
|
||
|
+
|
||
|
+ offset = phdr.p_offset;
|
||
|
+ if (!readmem(PADDR, offset, &nhdr, sizeof(nhdr)))
|
||
|
+ return FALSE;
|
||
|
+
|
||
|
+ offset += divideup(sizeof(Elf64_Nhdr), sizeof(Elf64_Word))*
|
||
|
+ sizeof(Elf64_Word);
|
||
|
+ memset(name, 0, sizeof(name));
|
||
|
+ if (!readmem(PADDR, offset, name, sizeof(name)))
|
||
|
+ return FALSE;
|
||
|
+
|
||
|
+ if(!strcmp(name, "VMCOREINFO")) {
|
||
|
+ nhdr_offset = offset;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!nhdr_offset)
|
||
|
+ return FALSE;
|
||
|
+
|
||
|
+ *addr = nhdr_offset +
|
||
|
+ divideup(nhdr.n_namesz, sizeof(Elf64_Word))*
|
||
|
+ sizeof(Elf64_Word);
|
||
|
+ *len = nhdr.n_descsz;
|
||
|
+
|
||
|
+ DEBUG_MSG("sadump: vmcoreinfo addr: %lx\n", *addr);
|
||
|
+ DEBUG_MSG("sadump: vmcoreinfo len: %d\n", *len);
|
||
|
+
|
||
|
+ return TRUE;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Check if current kaslr_offset/phys_base is for 1st kernel or 2nd kernel.
|
||
|
+ * If we are in 2nd kernel, get kaslr_offset/phys_base from vmcoreinfo.
|
||
|
+ *
|
||
|
+ * 1. Get command line and try to retrieve "elfcorehdr=" boot parameter
|
||
|
+ * 2. If "elfcorehdr=" is not found in command line, we are in 1st kernel.
|
||
|
+ * There is nothing to do.
|
||
|
+ * 3. If "elfcorehdr=" is found, we are in 2nd kernel. Find vmcoreinfo
|
||
|
+ * using "elfcorehdr=" and retrieve kaslr_offset/phys_base from vmcoreinfo.
|
||
|
+ */
|
||
|
+int
|
||
|
+get_kaslr_offset_from_vmcoreinfo(ulong cr3, ulong *kaslr_offset,
|
||
|
+ ulong *phys_base)
|
||
|
+{
|
||
|
+ ulong elfcorehdr_addr = 0;
|
||
|
+ ulong vmcoreinfo_addr;
|
||
|
+ int vmcoreinfo_len;
|
||
|
+ char *buf, *pos;
|
||
|
+ int ret = FALSE;
|
||
|
+
|
||
|
+ elfcorehdr_addr = get_elfcorehdr(cr3);
|
||
|
+ if (!elfcorehdr_addr)
|
||
|
+ return FALSE;
|
||
|
+
|
||
|
+ if (!get_vmcoreinfo_in_kdump_kernel(elfcorehdr_addr, &vmcoreinfo_addr,
|
||
|
+ &vmcoreinfo_len))
|
||
|
+ return FALSE;
|
||
|
+
|
||
|
+ if (!vmcoreinfo_len)
|
||
|
+ return FALSE;
|
||
|
+
|
||
|
+ DEBUG_MSG("sadump: Find vmcoreinfo in kdump memory\n");
|
||
|
+
|
||
|
+ if (!(buf = malloc(vmcoreinfo_len))) {
|
||
|
+ ERRMSG("Can't allocate vmcoreinfo buffer.\n");
|
||
|
+ return FALSE;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!readmem(PADDR, vmcoreinfo_addr, buf, vmcoreinfo_len))
|
||
|
+ goto finish;
|
||
|
+
|
||
|
+ pos = strstr(buf, STR_NUMBER("phys_base"));
|
||
|
+ if (!pos)
|
||
|
+ goto finish;
|
||
|
+ *phys_base = strtoull(pos + strlen(STR_NUMBER("phys_base")), NULL, 0);
|
||
|
+
|
||
|
+ pos = strstr(buf, STR_KERNELOFFSET);
|
||
|
+ if (!pos)
|
||
|
+ goto finish;
|
||
|
+ *kaslr_offset = strtoull(pos + strlen(STR_KERNELOFFSET), NULL, 16);
|
||
|
+ ret = TRUE;
|
||
|
+
|
||
|
+finish:
|
||
|
+ free(buf);
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
* Calculate kaslr_offset and phys_base
|
||
|
*
|
||
|
* kaslr_offset:
|
||
|
@@ -1106,6 +1341,26 @@ get_vec0_addr(ulong idtr)
|
||
|
*
|
||
|
* Note that the address (A) cannot be used instead of (E) because (A) is
|
||
|
* not direct map address, it's a fixed map address.
|
||
|
+ *
|
||
|
+ * This solution works in most every case, but does not work in the
|
||
|
+ * following case.
|
||
|
+ *
|
||
|
+ * 1) If the dump is captured on early stage of kernel boot, IDTR points
|
||
|
+ * early IDT table(early_idts) instead of normal IDT(idt_table).
|
||
|
+ * 2) If the dump is captured whle kdump is working, IDTR points
|
||
|
+ * IDT table of 2nd kernel, not 1st kernel.
|
||
|
+ *
|
||
|
+ * Current implementation does not support the case 1), need
|
||
|
+ * enhancement in the future. For the case 2), get kaslr_offset and
|
||
|
+ * phys_base as follows.
|
||
|
+ *
|
||
|
+ * 1) Get kaslr_offset and phys_base using the above solution.
|
||
|
+ * 2) Get kernel boot parameter from "saved_command_line"
|
||
|
+ * 3) If "elfcorehdr=" is not included in boot parameter, we are in the
|
||
|
+ * first kernel, nothing to do any more.
|
||
|
+ * 4) If "elfcorehdr=" is included in boot parameter, we are in the 2nd
|
||
|
+ * kernel. Retrieve vmcoreinfo from address of "elfcorehdr=" and
|
||
|
+ * get kaslr_offset and phys_base from vmcoreinfo.
|
||
|
*/
|
||
|
int
|
||
|
calc_kaslr_offset(void)
|
||
|
@@ -1116,6 +1371,7 @@ calc_kaslr_offset(void)
|
||
|
int apicid;
|
||
|
unsigned long divide_error_vmcore, divide_error_vmlinux;
|
||
|
unsigned long kaslr_offset, phys_base;
|
||
|
+ unsigned long kaslr_offset_kdump, phys_base_kdump;
|
||
|
|
||
|
memset(&zero, 0, sizeof(zero));
|
||
|
for (apicid = 0; apicid < sh->nr_cpus; ++apicid) {
|
||
|
@@ -1161,6 +1417,21 @@ calc_kaslr_offset(void)
|
||
|
if (!get_symbol_info())
|
||
|
return FALSE;
|
||
|
|
||
|
+ /*
|
||
|
+ * Check if current kaslr_offset/phys_base is for 1st kernel or 2nd
|
||
|
+ * kernel. If we are in 2nd kernel, get kaslr_offset/phys_base
|
||
|
+ * from vmcoreinfo
|
||
|
+ */
|
||
|
+ if (get_kaslr_offset_from_vmcoreinfo(cr3, &kaslr_offset_kdump,
|
||
|
+ &phys_base_kdump)) {
|
||
|
+ info->kaslr_offset = kaslr_offset_kdump;
|
||
|
+ info->phys_base = phys_base_kdump;
|
||
|
+
|
||
|
+ /* 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);
|
||
|
|
||
|
--
|
||
|
2.5.5
|
||
|
|