[Crash-utility] [PATCH v2 3/3] kaslr: get offset by walking page tree

Alexey Makhalov amakhalov at vmware.com
Tue Oct 27 01:07:31 UTC 2020


This method requires only valid CR3. It walks through
page tree starting from __START_KERNEL_map to get real
_stext and its physical address.
_stext_vmlinux has to be initialized. So, requesting it
by "st->_stext_vmlinux = UNINITIALIZED;" for sadump and
vmware backends. Other backends may use "--kaslr=auto"
to get similar effect.

This method is used as backup one for getting 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 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 symbols.c      |   4 +-
 2 files changed, 118 insertions(+), 1 deletion(-)

diff --git a/kaslr_helper.c b/kaslr_helper.c
index 02a7e48..d06ee44 100644
--- a/kaslr_helper.c
+++ b/kaslr_helper.c
@@ -323,6 +323,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.
+ */
+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:
@@ -449,6 +547,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())
@@ -509,6 +623,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);
diff --git a/symbols.c b/symbols.c
index 70b1455..430459a 100644
--- a/symbols.c
+++ b/symbols.c
@@ -634,8 +634,10 @@ kaslr_init(void)
 		}
 	}
 
-	if (SADUMP_DUMPFILE() || VMSS_DUMPFILE())
+	if (SADUMP_DUMPFILE() || VMSS_DUMPFILE()) {
 		kt->flags2 |= KASLR_CHECK;
+		st->_stext_vmlinux = UNINITIALIZED;
+	}
 }
 
 /*
-- 
2.11.0




More information about the Crash-utility mailing list