⬢ DragonFlyBSD Kernel Audit
DF-5358 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/platform/pc64/x86_64/pmap.c b/sys/platform/pc64/x86_64/pmap.c
index a301274..bcd8164 100644
--- a/sys/platform/pc64/x86_64/pmap.c
+++ b/sys/platform/pc64/x86_64/pmap.c
@@ -1864,15 +1864,46 @@ pmap_fault_page_quick(pmap_t pmap, vm_offset_t va, vm_prot_t prot, int *busyp)
 vm_paddr_t
 pmap_kextract(vm_offset_t va)
 {
-	pd_entry_t pt;		/* pt entry in pd */
+	pml4_entry_t pdp;	/* pml4e value */
+	pdp_entry_t pdpe;	/* pdp entry value */
+	pd_entry_t pde;		/* pd entry value */
 	vm_paddr_t pa;
 
 	if (va >= DMAP_MIN_ADDRESS && va < DMAP_MAX_ADDRESS) {
 		pa = DMAP_TO_PHYS(va);
 	} else {
-		pt = *vtopt(va);
-		if (pt & kernel_pmap->pmap_bits[PG_PS_IDX]) {
-			pa = (pt & PG_PS_FRAME) | (va & PDRMASK);
+		/*
+		 * Walk the kernel page tables through the DMAP instead of
+		 * dereferencing the recursive map.  Kernel page table pages
+		 * are allocated on demand (pmap_growkernel()), so for a KVA
+		 * whose upper levels have not been allocated yet the
+		 * recursive-map address of the PDE slot is itself unmapped:
+		 * *vtopt(va) took an unrecoverable kernel page fault with
+		 * no onfault (reachable from /dev/kmem through
+		 * kvm_access_check(), vm_machdep.c).
+		 *
+		 * Also require PG_V at each level so an unmapped VA returns
+		 * 0 instead of a bogus (va & PAGE_MASK)-based physical
+		 * address; that is what makes kvm_access_check()'s per-page
+		 * residency test meaningful.
+		 */
+		pdp = *pmap_pdp(kernel_pmap, va);
+		if ((pdp & kernel_pmap->pmap_bits[PG_V_IDX]) == 0)
+			return 0;
+		pdpe = *pmap_pdp_to_pd(pdp, va);
+		if ((pdpe & kernel_pmap->pmap_bits[PG_V_IDX]) == 0)
+			return 0;
+		if (pdpe & kernel_pmap->pmap_bits[PG_PS_IDX]) {
+			/* 1GB page (not used by the kernel pmap today) */
+			pa = (pdpe & 0x000FFFFFC0000000UL) |
+			     (va & ((1UL << PDPSHIFT) - 1));
+			return pa;
+		}
+		pde = *pmap_pd_to_pt(pdpe, va);
+		if ((pde & kernel_pmap->pmap_bits[PG_V_IDX]) == 0)
+			return 0;
+		if (pde & kernel_pmap->pmap_bits[PG_PS_IDX]) {
+			pa = (pde & PG_PS_FRAME) | (va & PDRMASK);
 		} else {
 			/*
 			 * Beware of a concurrent promotion that changes the
@@ -1882,7 +1913,9 @@ pmap_kextract(vm_offset_t va)
 			 * because the page table page is preserved by the
 			 * promotion.
 			 */
-			pa = *pmap_pt_to_pte(pt, va);
+			pa = *pmap_pt_to_pte(pde, va);
+			if ((pa & kernel_pmap->pmap_bits[PG_V_IDX]) == 0)
+				return 0;
 			pa = (pa & PG_FRAME) | (va & PAGE_MASK);
 		}
 	}