Blame SOURCES/kexec-tools-2.0.15-makedumpfile-sadump-Fix-a-KASLR-problem-of-sadump-wh.patch

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