|
|
f8bec6 |
From 3c0cf7a93cff83f1e711e241eb47fcb096a451c5 Mon Sep 17 00:00:00 2001
|
|
|
f8bec6 |
From: HATAYAMA Daisuke <d.hatayama@fujitsu.com>
|
|
|
f8bec6 |
Date: Thu, 9 Jul 2020 18:27:49 +0900
|
|
|
f8bec6 |
Subject: [PATCH] [PATCH] sadump, kaslr: fix failure of calculating
|
|
|
f8bec6 |
kaslr_offset due to an sadump format restriction
|
|
|
f8bec6 |
|
|
|
f8bec6 |
We faced recently a memory dump collected by sadump where unused part
|
|
|
f8bec6 |
of register values are non-zero. For the crash dump, calculating
|
|
|
f8bec6 |
kaslr_offset fails because it is based on the assumption that unused
|
|
|
f8bec6 |
part of register values in the sadump format are always zero cleared.
|
|
|
f8bec6 |
|
|
|
f8bec6 |
The problem is that used and unused part of register values are
|
|
|
f8bec6 |
rigorously indistinguishable in the sadump format. Although there is
|
|
|
f8bec6 |
kernel data structure that represents a map between logical cpu
|
|
|
f8bec6 |
numbers and lapic ids, they cannot be used in order to calculate
|
|
|
f8bec6 |
kaslr_offset.
|
|
|
f8bec6 |
|
|
|
f8bec6 |
To fix this, we have no choice but use a trial-and-error approach: try
|
|
|
f8bec6 |
to use each entry of register values in order until we find a good
|
|
|
f8bec6 |
pair of cr3 and idtr by which we can refer to linux_banner symbol as
|
|
|
f8bec6 |
expected.
|
|
|
f8bec6 |
|
|
|
f8bec6 |
Signed-off-by: HATAYAMA Daisuke <d.hatayama@fujitsu.com>
|
|
|
f8bec6 |
---
|
|
|
f8bec6 |
sadump_info.c | 129 +++++++++++++++++++++++++++++++++++++++++-----------------
|
|
|
f8bec6 |
1 file changed, 91 insertions(+), 38 deletions(-)
|
|
|
f8bec6 |
|
|
|
f8bec6 |
diff --git a/makedumpfile-1.6.7/sadump_info.c b/makedumpfile-1.6.7/sadump_info.c
|
|
|
f8bec6 |
index 72a077b4f408..410c6bc2a909 100644
|
|
|
f8bec6 |
--- a/makedumpfile-1.6.7/sadump_info.c
|
|
|
f8bec6 |
+++ b/makedumpfile-1.6.7/sadump_info.c
|
|
|
f8bec6 |
@@ -101,6 +101,7 @@ static int lookup_diskset(unsigned long long whole_offset, int *diskid,
|
|
|
f8bec6 |
unsigned long long *disk_offset);
|
|
|
f8bec6 |
static int max_mask_cpu(void);
|
|
|
f8bec6 |
static int cpu_online_mask_init(void);
|
|
|
f8bec6 |
+static int linux_banner_sanity_check(ulong cr3);
|
|
|
f8bec6 |
static int per_cpu_init(void);
|
|
|
f8bec6 |
static int get_data_from_elf_note_desc(const char *note_buf, uint32_t n_descsz,
|
|
|
f8bec6 |
char *name, uint32_t n_type, char **data);
|
|
|
f8bec6 |
@@ -1293,6 +1294,30 @@ finish:
|
|
|
f8bec6 |
return ret;
|
|
|
f8bec6 |
}
|
|
|
f8bec6 |
|
|
|
f8bec6 |
+static int linux_banner_sanity_check(ulong cr3)
|
|
|
f8bec6 |
+{
|
|
|
f8bec6 |
+ unsigned long linux_banner_paddr;
|
|
|
f8bec6 |
+ char buf[sizeof("Linux version")];
|
|
|
f8bec6 |
+
|
|
|
f8bec6 |
+ linux_banner_paddr = vtop4_x86_64_pagetable(SYMBOL(linux_banner), cr3);
|
|
|
f8bec6 |
+ if (linux_banner_paddr == NOT_PADDR) {
|
|
|
f8bec6 |
+ DEBUG_MSG("sadump: linux_banner address translation failed\n");
|
|
|
f8bec6 |
+ return FALSE;
|
|
|
f8bec6 |
+ }
|
|
|
f8bec6 |
+
|
|
|
f8bec6 |
+ if (!readmem(PADDR, linux_banner_paddr, &buf, sizeof(buf))) {
|
|
|
f8bec6 |
+ DEBUG_MSG("sadump: reading linux_banner failed\n");
|
|
|
f8bec6 |
+ return FALSE;
|
|
|
f8bec6 |
+ }
|
|
|
f8bec6 |
+
|
|
|
f8bec6 |
+ if (!STRNEQ(buf, "Linux version")) {
|
|
|
f8bec6 |
+ DEBUG_MSG("sadump: linux_banner sanity check failed\n");
|
|
|
f8bec6 |
+ return FALSE;
|
|
|
f8bec6 |
+ }
|
|
|
f8bec6 |
+
|
|
|
f8bec6 |
+ return TRUE;
|
|
|
f8bec6 |
+}
|
|
|
f8bec6 |
+
|
|
|
f8bec6 |
/*
|
|
|
f8bec6 |
* Calculate kaslr_offset and phys_base
|
|
|
f8bec6 |
*
|
|
|
f8bec6 |
@@ -1370,59 +1395,85 @@ calc_kaslr_offset(void)
|
|
|
f8bec6 |
{
|
|
|
f8bec6 |
struct sadump_header *sh = si->sh_memory;
|
|
|
f8bec6 |
uint64_t idtr = 0, cr3 = 0, idtr_paddr;
|
|
|
f8bec6 |
- struct sadump_smram_cpu_state smram, zero;
|
|
|
f8bec6 |
+ struct sadump_smram_cpu_state smram;
|
|
|
f8bec6 |
int apicid;
|
|
|
f8bec6 |
unsigned long divide_error_vmcore, divide_error_vmlinux;
|
|
|
f8bec6 |
unsigned long kaslr_offset, phys_base;
|
|
|
f8bec6 |
unsigned long kaslr_offset_kdump, phys_base_kdump;
|
|
|
f8bec6 |
+ int sanity_check_passed = FALSE;
|
|
|
f8bec6 |
|
|
|
f8bec6 |
- memset(&zero, 0, sizeof(zero));
|
|
|
f8bec6 |
for (apicid = 0; apicid < sh->nr_cpus; ++apicid) {
|
|
|
f8bec6 |
+
|
|
|
f8bec6 |
+ DEBUG_MSG("sadump: apicid: %d\n", apicid);
|
|
|
f8bec6 |
+
|
|
|
f8bec6 |
if (!get_smram_cpu_state(apicid, &smram)) {
|
|
|
f8bec6 |
ERRMSG("get_smram_cpu_state error\n");
|
|
|
f8bec6 |
return FALSE;
|
|
|
f8bec6 |
}
|
|
|
f8bec6 |
|
|
|
f8bec6 |
- if (memcmp(&smram, &zero, sizeof(smram)) != 0)
|
|
|
f8bec6 |
- break;
|
|
|
f8bec6 |
- }
|
|
|
f8bec6 |
- if (apicid >= sh->nr_cpus) {
|
|
|
f8bec6 |
- ERRMSG("Can't get smram state\n");
|
|
|
f8bec6 |
- return FALSE;
|
|
|
f8bec6 |
- }
|
|
|
f8bec6 |
+ idtr = ((uint64_t)smram.IdtUpper)<<32|(uint64_t)smram.IdtLower;
|
|
|
f8bec6 |
|
|
|
f8bec6 |
- idtr = ((uint64_t)smram.IdtUpper)<<32 | (uint64_t)smram.IdtLower;
|
|
|
f8bec6 |
- if ((SYMBOL(pti_init) != NOT_FOUND_SYMBOL) ||
|
|
|
f8bec6 |
- (SYMBOL(kaiser_init) != NOT_FOUND_SYMBOL))
|
|
|
f8bec6 |
- cr3 = smram.Cr3 & ~(CR3_PCID_MASK|PTI_USER_PGTABLE_MASK);
|
|
|
f8bec6 |
- else
|
|
|
f8bec6 |
- cr3 = smram.Cr3 & ~CR3_PCID_MASK;
|
|
|
f8bec6 |
+ if (!smram.Cr3 || !idtr) {
|
|
|
f8bec6 |
+ DEBUG_MSG("sadump: cr3: %lx idt: %lx, skipped\n",
|
|
|
f8bec6 |
+ smram.Cr3, idtr);
|
|
|
f8bec6 |
+ continue;
|
|
|
f8bec6 |
+ }
|
|
|
f8bec6 |
|
|
|
f8bec6 |
- /* Convert virtual address of IDT table to physical address */
|
|
|
f8bec6 |
- if ((idtr_paddr = vtop4_x86_64_pagetable(idtr, cr3)) == NOT_PADDR)
|
|
|
f8bec6 |
- return FALSE;
|
|
|
f8bec6 |
+ if ((SYMBOL(pti_init) != NOT_FOUND_SYMBOL) ||
|
|
|
f8bec6 |
+ (SYMBOL(kaiser_init) != NOT_FOUND_SYMBOL))
|
|
|
f8bec6 |
+ cr3 = smram.Cr3 & ~(CR3_PCID_MASK|PTI_USER_PGTABLE_MASK);
|
|
|
f8bec6 |
+ else
|
|
|
f8bec6 |
+ cr3 = smram.Cr3 & ~CR3_PCID_MASK;
|
|
|
f8bec6 |
|
|
|
f8bec6 |
- /* Now we can calculate kaslr_offset and phys_base */
|
|
|
f8bec6 |
- divide_error_vmlinux = SYMBOL(divide_error);
|
|
|
f8bec6 |
- divide_error_vmcore = get_vec0_addr(idtr_paddr);
|
|
|
f8bec6 |
- kaslr_offset = divide_error_vmcore - divide_error_vmlinux;
|
|
|
f8bec6 |
- phys_base = idtr_paddr -
|
|
|
f8bec6 |
- (SYMBOL(idt_table) + kaslr_offset - __START_KERNEL_map);
|
|
|
f8bec6 |
+ /* Convert virtual address of IDT table to physical address */
|
|
|
f8bec6 |
+ idtr_paddr = vtop4_x86_64_pagetable(idtr, cr3);
|
|
|
f8bec6 |
+ if (idtr_paddr == NOT_PADDR) {
|
|
|
f8bec6 |
+ DEBUG_MSG("sadump: converting IDT physical address "
|
|
|
f8bec6 |
+ "failed.\n");
|
|
|
f8bec6 |
+ continue;
|
|
|
f8bec6 |
+ }
|
|
|
f8bec6 |
|
|
|
f8bec6 |
- info->kaslr_offset = kaslr_offset;
|
|
|
f8bec6 |
- info->phys_base = phys_base;
|
|
|
f8bec6 |
+ /* Now we can calculate kaslr_offset and phys_base */
|
|
|
f8bec6 |
+ divide_error_vmlinux = SYMBOL(divide_error);
|
|
|
f8bec6 |
+ divide_error_vmcore = get_vec0_addr(idtr_paddr);
|
|
|
f8bec6 |
+ kaslr_offset = divide_error_vmcore - divide_error_vmlinux;
|
|
|
f8bec6 |
+ phys_base = idtr_paddr -
|
|
|
f8bec6 |
+ (SYMBOL(idt_table)+kaslr_offset-__START_KERNEL_map);
|
|
|
f8bec6 |
|
|
|
f8bec6 |
- DEBUG_MSG("sadump: idtr=%" PRIx64 "\n", idtr);
|
|
|
f8bec6 |
- DEBUG_MSG("sadump: cr3=%" PRIx64 "\n", cr3);
|
|
|
f8bec6 |
- DEBUG_MSG("sadump: idtr(phys)=%" PRIx64 "\n", idtr_paddr);
|
|
|
f8bec6 |
- DEBUG_MSG("sadump: devide_error(vmlinux)=%lx\n",
|
|
|
f8bec6 |
- divide_error_vmlinux);
|
|
|
f8bec6 |
- DEBUG_MSG("sadump: devide_error(vmcore)=%lx\n",
|
|
|
f8bec6 |
- divide_error_vmcore);
|
|
|
f8bec6 |
+ info->kaslr_offset = kaslr_offset;
|
|
|
f8bec6 |
+ info->phys_base = phys_base;
|
|
|
f8bec6 |
|
|
|
f8bec6 |
- /* Reload symbol */
|
|
|
f8bec6 |
- if (!get_symbol_info())
|
|
|
f8bec6 |
- return FALSE;
|
|
|
f8bec6 |
+ DEBUG_MSG("sadump: idtr=%" PRIx64 "\n", idtr);
|
|
|
f8bec6 |
+ DEBUG_MSG("sadump: cr3=%" PRIx64 "\n", cr3);
|
|
|
f8bec6 |
+ DEBUG_MSG("sadump: idtr(phys)=%" PRIx64 "\n", idtr_paddr);
|
|
|
f8bec6 |
+ DEBUG_MSG("sadump: devide_error(vmlinux)=%lx\n",
|
|
|
f8bec6 |
+ divide_error_vmlinux);
|
|
|
f8bec6 |
+ DEBUG_MSG("sadump: devide_error(vmcore)=%lx\n",
|
|
|
f8bec6 |
+ divide_error_vmcore);
|
|
|
f8bec6 |
+
|
|
|
f8bec6 |
+ /* Reload symbol */
|
|
|
f8bec6 |
+ if (!get_symbol_info()) {
|
|
|
f8bec6 |
+ ERRMSG("Reading symbol table failed\n");
|
|
|
f8bec6 |
+ return FALSE;
|
|
|
f8bec6 |
+ }
|
|
|
f8bec6 |
+
|
|
|
f8bec6 |
+ /* Sanity check */
|
|
|
f8bec6 |
+ if (linux_banner_sanity_check(cr3)) {
|
|
|
f8bec6 |
+ sanity_check_passed = TRUE;
|
|
|
f8bec6 |
+ break;
|
|
|
f8bec6 |
+ }
|
|
|
f8bec6 |
+
|
|
|
f8bec6 |
+ info->kaslr_offset = 0;
|
|
|
f8bec6 |
+ info->phys_base = 0;
|
|
|
f8bec6 |
+ }
|
|
|
f8bec6 |
+
|
|
|
f8bec6 |
+ if (!sanity_check_passed) {
|
|
|
f8bec6 |
+ ERRMSG("failed to calculate kaslr_offset and phys_base; "
|
|
|
f8bec6 |
+ "default to 0\n");
|
|
|
f8bec6 |
+ info->kaslr_offset = 0;
|
|
|
f8bec6 |
+ info->phys_base = 0;
|
|
|
f8bec6 |
+ return TRUE;
|
|
|
f8bec6 |
+ }
|
|
|
f8bec6 |
|
|
|
f8bec6 |
/*
|
|
|
f8bec6 |
* Check if current kaslr_offset/phys_base is for 1st kernel or 2nd
|
|
|
f8bec6 |
@@ -1430,13 +1481,15 @@ calc_kaslr_offset(void)
|
|
|
f8bec6 |
* from vmcoreinfo
|
|
|
f8bec6 |
*/
|
|
|
f8bec6 |
if (get_kaslr_offset_from_vmcoreinfo(cr3, &kaslr_offset_kdump,
|
|
|
f8bec6 |
- &phys_base_kdump)) {
|
|
|
f8bec6 |
+ &phys_base_kdump)) {
|
|
|
f8bec6 |
info->kaslr_offset = kaslr_offset_kdump;
|
|
|
f8bec6 |
info->phys_base = phys_base_kdump;
|
|
|
f8bec6 |
|
|
|
f8bec6 |
/* Reload symbol */
|
|
|
f8bec6 |
- if (!get_symbol_info())
|
|
|
f8bec6 |
+ if (!get_symbol_info()) {
|
|
|
f8bec6 |
+ ERRMSG("Reading symbol table failed\n");
|
|
|
f8bec6 |
return FALSE;
|
|
|
f8bec6 |
+ }
|
|
|
f8bec6 |
}
|
|
|
f8bec6 |
|
|
|
f8bec6 |
DEBUG_MSG("sadump: kaslr_offset=%lx\n", info->kaslr_offset);
|
|
|
f8bec6 |
--
|
|
|
f8bec6 |
2.7.4
|
|
|
f8bec6 |
|