[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