[Crash-utility] [PATCH 2/2] kaslr: get offset by walking page tree
lijiang
lijiang at redhat.com
Wed Oct 21 11:42:37 UTC 2020
Hi, Alexey
Thanks for the patch.
在 2020年10月17日 00:00, crash-utility-request at redhat.com 写道:
> Date: Thu, 15 Oct 2020 13:44:32 -0700
> From: Alexey Makhalov <amakhalov at vmware.com>
> To: <crash-utility at redhat.com>, <amakhalov at vmware.com>
> Subject: [Crash-utility] [PATCH 2/2] kaslr: get offset by walking page
> tree
> Message-ID: <20201015204432.4695-3-amakhalov at vmware.com>
> Content-Type: text/plain
>
> This method requires only valid CR3. It walks through
> page tree starting from __START_KERNEL_map to get real
> _stext and its physical address.
> It is used as backup method to get kaslr offset, if
> IDTR is not valid (zeroed). It might happen when kernel
> invalidates IDT, for example triggering triple fault on
> reboot (reboot=t cmdline).
>
> This method does not support PTI (Page Table Isolation)
> case where CR3 points to the isolated page tree. So, use
> it only when CR3 points to "full" kernel.
>
> Signed-off-by: Alexey Makhalov <amakhalov at vmware.com>
> ---
> kaslr_helper.c | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 115 insertions(+)
>
> diff --git a/kaslr_helper.c b/kaslr_helper.c
> index bb19e54..f11cb55 100644
> --- a/kaslr_helper.c
> +++ b/kaslr_helper.c
> @@ -322,6 +322,104 @@ quit:
> }
>
> /*
> + * Find virtual (VA) and physical (PA) addresses of kernel start
> + *
> + * va:
> + * Actual address of the kernel start (_stext) placed
> + * randomly by kaslr feature. To be more accurate,
> + * VA = _stext(from vmlinux) + kaslr_offset
> + *
> + * pa:
> + * Physical address where the kerenel is placed.
> + *
> + * In nokaslr case, VA = _stext (from vmlinux)
> + * In kaslr case, virtual address of the kernel placement goes
> + * in this range: ffffffff80000000..ffffffff9fffffff, or
> + * __START_KERNEL_map..+512MB
> + *
> + * https://www.kernel.org/doc/Documentation/x86/x86_64/mm.txt
> + *
> + * Randomized VA will be the first valid page starting from
> + * ffffffff80000000 (__START_KERNEL_map). Page tree entry of
> + * this page will contain the PA of the kernel start.
> + *
> + * NOTES:
> + * 1. This method does not support PTI (Page Table Isolation)
> + * case where CR3 points to the isolated page tree.
> + * 2. 4-level paging support only, as caller (calc_kaslr_offset)
> + * does not support 5-level paging.
Would it be good to add a flag checking for the 5-level paging before calling
the find_kernel_start()? Seems that we can not check the machdep->flags with
the 'machdep->flags & VM_5LEVEL', because that is not initialized.
But, is that possible to check the CR4(vmss.regs64[0]->cr[4]) on x86 or the
symbols '__pgtable_l5_enabled'?
Thanks.
Lianbo
> + */
> +static int
> +find_kernel_start(ulong *va, ulong *pa)
> +{
> + int i, pgd_idx, pud_idx, pmd_idx, pte_idx;
> + uint64_t pgd_pte, pud_pte, pmd_pte, pte;
> +
> + pgd_idx = pgd_index(__START_KERNEL_map);
> + pud_idx = pud_index(__START_KERNEL_map);
> + pmd_idx = pmd_index(__START_KERNEL_map);
> + pte_idx = pte_index(__START_KERNEL_map);
> +
> + for (; pgd_idx < PTRS_PER_PGD; pgd_idx++) {
> + pgd_pte = ULONG(machdep->pgd + pgd_idx * sizeof(uint64_t));
> + if (pgd_pte & _PAGE_PRESENT)
> + break;
> + pud_idx = pmd_idx = pte_idx = 0;
> + }
> + if (pgd_idx == PTRS_PER_PGD)
> + return FALSE;
> +
> + FILL_PUD(pgd_pte & PHYSICAL_PAGE_MASK, PHYSADDR, PAGESIZE());
> + for (; pud_idx < PTRS_PER_PUD; pud_idx++) {
> + pud_pte = ULONG(machdep->pud + pud_idx * sizeof(uint64_t));
> + if (pud_pte & _PAGE_PRESENT)
> + break;
> + pmd_idx = pte_idx = 0;
> + }
> + if (pud_idx == PTRS_PER_PUD)
> + return FALSE;
> + if (pud_pte & _PAGE_PSE) {
> + /* 1GB page */
> + *va = (~__VIRTUAL_MASK) | ((ulong)pgd_idx << __PGDIR_SHIFT) |
> + ((ulong)pud_idx << PUD_SHIFT);
> + *pa = pud_pte & PHYSICAL_PAGE_MASK;
> + return TRUE;
> + }
> +
> + FILL_PMD(pud_pte & PHYSICAL_PAGE_MASK, PHYSADDR, PAGESIZE());
> + for (; pmd_idx < PTRS_PER_PMD; pmd_idx++) {
> + pmd_pte = ULONG(machdep->pmd + pmd_idx * sizeof(uint64_t));
> + if (pmd_pte & _PAGE_PRESENT)
> + break;
> + pte_idx = 0;
> + }
> + if (pmd_idx == PTRS_PER_PMD)
> + return FALSE;
> + if (pmd_pte & _PAGE_PSE) {
> + /* 2MB page */
> + *va = (~__VIRTUAL_MASK) | ((ulong)pgd_idx << __PGDIR_SHIFT) |
> + ((ulong)pud_idx << PUD_SHIFT) | (pmd_idx << PMD_SHIFT);
> + *pa = pmd_pte & PHYSICAL_PAGE_MASK;
> + return TRUE;
> + }
> +
> + FILL_PTBL(pmd_pte & PHYSICAL_PAGE_MASK, PHYSADDR, PAGESIZE());
> + for (; pte_idx < PTRS_PER_PTE; pte_idx++) {
> + pte = ULONG(machdep->ptbl + pte_idx * sizeof(uint64_t));
> + if (pte & _PAGE_PRESENT)
> + break;
> + }
> + if (pte_idx == PTRS_PER_PTE)
> + return FALSE;
> +
> + *va = (~__VIRTUAL_MASK) | ((ulong)pgd_idx << __PGDIR_SHIFT) |
> + ((ulong)pud_idx << PUD_SHIFT) | (pmd_idx << PMD_SHIFT) |
> + (pte_idx << PAGE_SHIFT);
> + *pa = pmd_pte & PHYSICAL_PAGE_MASK;
> + return TRUE;
> +}
> +
> +/*
> * Calculate kaslr_offset and phys_base
> *
> * kaslr_offset:
> @@ -445,6 +543,22 @@ retry:
> goto quit;
> }
>
> + if (idtr == 0 && st->_stext_vmlinux && (st->_stext_vmlinux != UNINITIALIZED)) {
> + ulong va, pa;
> + ret = find_kernel_start(&va, &pa);
> + if (ret == FALSE)
> + goto quit;
> + if (CRASHDEBUG(1)) {
> + fprintf(fp, "calc_kaslr_offset: _stext(vmlinux): %lx\n", st->_stext_vmlinux);
> + fprintf(fp, "calc_kaslr_offset: kernel start VA: %lx\n", va);
> + fprintf(fp, "calc_kaslr_offset: kernel start PA: %lx\n", pa);
> + }
> + kaslr_offset = va - st->_stext_vmlinux;
> + phys_base = pa - (va - __START_KERNEL_map);
> +
> + goto found;
> + }
> +
> /* Convert virtual address of IDT table to physical address */
> if (!kvtop(NULL, idtr, &idtr_paddr, verbose)) {
> if (SADUMP_DUMPFILE())
> @@ -505,6 +619,7 @@ retry:
> fprintf(fp, "kaslr_helper: asssuming the kdump 1st kernel.\n");
> }
>
> +found:
> if (CRASHDEBUG(1)) {
> fprintf(fp, "calc_kaslr_offset: kaslr_offset=%lx\n",
> kaslr_offset);
> -- 2.11.0
More information about the Crash-utility
mailing list