[PATCH] sadump, kaslr: fix failure of calculating kaslr_offset due to an sadump format restriction
Kazuhito Hagio
kazuhito.hagio at gmail.com
Fri Jul 10 03:08:35 EDT 2020
On Thu, Jul 9, 2020 at 6:30 PM HATAYAMA Daisuke <d.hatayama at fujitsu.com> wrote:
>
> We faced recently a memory dump collected by sadump where unused part
> of register values are non-zero. For the crash dump, calculating
> kaslr_offset fails because it is based on the assumption that unused
> part of register values in the sadump format are always zero cleared.
>
> The problem is that used and unused part of register values are
> rigorously indistinguishable in the sadump format. Although there is
> kernel data structure that represents a map between logical cpu
> numbers and lapic ids, they cannot be used in order to calculate
> kaslr_offset.
>
> To fix this, we have no choice but use a trial-and-error approach: try
> to use each entry of register values in order until we find a good
> pair of cr3 and idtr by which we can refer to linux_banner symbol as
> expected.
>
> Signed-off-by: HATAYAMA Daisuke <d.hatayama at fujitsu.com>
I've replaced some indent spaces with tabs and merged:
https://github.com/makedumpfile/makedumpfile/commit/3c0cf7a93cff83f1e711e241eb47fcb096a451c5
Thanks,
Kazu
> ---
> sadump_info.c | 140 ++++++++++++++++++++++++++++++++++++++++------------------
> 1 file changed, 97 insertions(+), 43 deletions(-)
>
> diff --git a/sadump_info.c b/sadump_info.c
> index 46867ce..aa9a048 100644
> --- a/sadump_info.c
> +++ b/sadump_info.c
> @@ -101,6 +101,7 @@ static int lookup_diskset(unsigned long long whole_offset, int *diskid,
> unsigned long long *disk_offset);
> static int max_mask_cpu(void);
> static int cpu_online_mask_init(void);
> +static int linux_banner_sanity_check(ulong cr3);
> static int per_cpu_init(void);
> static int get_data_from_elf_note_desc(const char *note_buf, uint32_t n_descsz,
> char *name, uint32_t n_type, char **data);
> @@ -1293,6 +1294,30 @@ finish:
> return ret;
> }
>
> +static int linux_banner_sanity_check(ulong cr3)
> +{
> + unsigned long linux_banner_paddr;
> + char buf[sizeof("Linux version")];
> +
> + linux_banner_paddr = vtop4_x86_64_pagetable(SYMBOL(linux_banner), cr3);
> + if (linux_banner_paddr == NOT_PADDR) {
> + DEBUG_MSG("sadump: linux_banner address translation failed\n");
> + return FALSE;
> + }
> +
> + if (!readmem(PADDR, linux_banner_paddr, &buf, sizeof(buf))) {
> + DEBUG_MSG("sadump: reading linux_banner failed\n");
> + return FALSE;
> + }
> +
> + if (!STRNEQ(buf, "Linux version")) {
> + DEBUG_MSG("sadump: linux_banner sanity check failed\n");
> + return FALSE;
> + }
> +
> + return TRUE;
> +}
> +
> /*
> * Calculate kaslr_offset and phys_base
> *
> @@ -1370,59 +1395,86 @@ 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;
> + struct sadump_smram_cpu_state smram;
> int apicid;
> unsigned long divide_error_vmcore, divide_error_vmlinux;
> unsigned long kaslr_offset, phys_base;
> unsigned long kaslr_offset_kdump, phys_base_kdump;
> + int sanity_check_passed = FALSE;
>
> - 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");
> +
> + DEBUG_MSG("sadump: apicid: %d\n", apicid);
> +
> + if (!get_smram_cpu_state(apicid, &smram)) {
> + ERRMSG("get_smram_cpu_state error\n");
> + return FALSE;
> + }
> +
> + idtr = ((uint64_t)smram.IdtUpper)<<32|(uint64_t)smram.IdtLower;
> +
> + if (!smram.Cr3 || !idtr) {
> + DEBUG_MSG("sadump: cr3: %lx idt: %lx, skipped\n",
> + smram.Cr3,
> + idtr);
> + continue;
> + }
> +
> + if ((SYMBOL(pti_init) != NOT_FOUND_SYMBOL) ||
> + (SYMBOL(kaiser_init) != NOT_FOUND_SYMBOL))
> + cr3 = smram.Cr3 & ~(CR3_PCID_MASK|PTI_USER_PGTABLE_MASK);
> + else
> + cr3 = smram.Cr3 & ~CR3_PCID_MASK;
> +
> + /* Convert virtual address of IDT table to physical address */
> + idtr_paddr = vtop4_x86_64_pagetable(idtr, cr3);
> + if (idtr_paddr == NOT_PADDR) {
> + DEBUG_MSG("sadump: converting IDT physical address "
> + "failed.\n");
> + continue;
> + }
> +
> + /* 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()) {
> + ERRMSG("Reading symbol table failed\n");
> return FALSE;
> }
>
> - if (memcmp(&smram, &zero, sizeof(smram)) != 0)
> + /* Sanity check */
> + if (linux_banner_sanity_check(cr3)) {
> + sanity_check_passed = TRUE;
> 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;
> - if ((SYMBOL(pti_init) != NOT_FOUND_SYMBOL) ||
> - (SYMBOL(kaiser_init) != NOT_FOUND_SYMBOL))
> - cr3 = smram.Cr3 & ~(CR3_PCID_MASK|PTI_USER_PGTABLE_MASK);
> - else
> - cr3 = smram.Cr3 & ~CR3_PCID_MASK;
> -
> - /* 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);
> + info->kaslr_offset = 0;
> + info->phys_base = 0;
> + }
>
> - /* Reload symbol */
> - if (!get_symbol_info())
> - return FALSE;
> + if (!sanity_check_passed) {
> + ERRMSG("failed to calculate kaslr_offset and phys_base; "
> + "default to 0\n");
> + info->kaslr_offset = 0;
> + info->phys_base = 0;
> + return TRUE;
> + }
>
> /*
> * Check if current kaslr_offset/phys_base is for 1st kernel or 2nd
> @@ -1430,13 +1482,15 @@ calc_kaslr_offset(void)
> * from vmcoreinfo
> */
> if (get_kaslr_offset_from_vmcoreinfo(cr3, &kaslr_offset_kdump,
> - &phys_base_kdump)) {
> + &phys_base_kdump)) {
> info->kaslr_offset = kaslr_offset_kdump;
> info->phys_base = phys_base_kdump;
>
> /* Reload symbol */
> - if (!get_symbol_info())
> + if (!get_symbol_info()) {
> + ERRMSG("Reading symbol table failed\n");
> return FALSE;
> + }
> }
>
> DEBUG_MSG("sadump: kaslr_offset=%lx\n", info->kaslr_offset);
> --
> 1.8.3.1
>
>
> _______________________________________________
> kexec mailing list
> kexec at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/kexec
More information about the kexec
mailing list