[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