DF-5801 / pqb_bits_rig.c
/* * DF-5801 KLD rig: vm_page_bits() / vm_page_set_valid*() have NO bounds * validation in production kernels (the only guard is a KASSERT, which is * compiled out unless INVARIANTS is set - sys/sys/systm.h:118). * * This rig demonstrates, against the running production kernel: * (1) vm_page_bits() accepts base/size outside the page and returns * masks with bits >= 8 set (and UB shifts for negative/huge args); * (2) vm_page_set_valid() fed an out-of-page (base,size) silently marks * the WHOLE page valid in its uint8_t m->valid, over-validating * fragments that were never populated - the stale-data / info-leak * direction if any caller ever miscalculates the range; * (3) the phantom bit-8 result is visible to int-sized consumers * (e.g. the m->dirty & vm_page_bits(...) test in vfs_bio.c). */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/module.h> #include <vm/vm.h> #include <vm/vm_page.h> #include <vm/vm_page2.h> #include <vm/vm_extern.h> #include <machine/pmap.h> static int rig_loader(struct module *m, int what, void *arg) { vm_page_t pg; char *p; int r_in, r_oob_size, r_oob_base, r_ub; switch (what) { case MOD_LOAD: /* ---- (1) mask function: no production bounds check ---- */ r_in = vm_page_bits(0, PAGE_SIZE); /* legal: 0xff */ r_oob_size = vm_page_bits(0, PAGE_SIZE * 2); /* size > page */ r_oob_base = vm_page_bits(PAGE_SIZE, 1); /* base > page */ kprintf("RIG bits(0,%d)=0x%x (in-page, legal)\n", PAGE_SIZE, r_in); kprintf("RIG bits(0,%d)=0x%x (size beyond page, unchecked)\n", PAGE_SIZE * 2, r_oob_size); kprintf("RIG bits(%d,1)=0x%x (base beyond page -> phantom bit 8," " visible to int consumers)\n", PAGE_SIZE, r_oob_base); r_ub = vm_page_bits(-512, 1024); /* UB: 1 << -1 */ kprintf("RIG bits(-512,1024)=0x%x (negative base/size ->" " shift UB, unguarded)\n", (unsigned)r_ub); /* ---- (2) setter over-validation on a real vm_page ---- */ pg = vm_page_alloczwq(0, VM_ALLOC_NORMAL | VM_ALLOC_SYSTEM | VM_ALLOC_ZERO); if (pg == NULL) { kprintf("RIG alloczwq failed\n"); return (0); } p = (char *)PHYS_TO_DMAP(VM_PAGE_TO_PHYS(pg)); memset(p, 0x41, PAGE_SIZE); /* simulate stale kernel data */ pg->valid = 0x0f; /* only first 4 fragments valid */ vm_page_busy_wait(pg, FALSE, "rigb"); /* caller asks to validate 4.5KB starting at 0 (range crosses * the page end). bits(0, PAGE_SIZE+512) = (2<<8)-1 = 0x1ff, * m->valid |= 0x1ff -> uint8_t truncates -> whole page (0xff) * marked valid, including the never-read last fragment. */ vm_page_set_valid(pg, 0, PAGE_SIZE + 512); kprintf("RIG set_valid(0,%d) on page w/ valid=0x0f ->" " m->valid=0x%02x (whole page 'valid', stale 0x41 tail" " now indistinguishable from validated data)\n", PAGE_SIZE + 512, pg->valid); kprintf("RIG tail fragment content still 0x41 (never zeroed:" " _vm_page_zero_valid only touches DEV_BSIZE" " boundaries)\n"); vm_page_wakeup(pg); vm_page_freezwq(pg); kprintf("RIG done\n"); return (0); default: return (0); } } static moduledata_t rig_mod = { "pqb_rig", rig_loader, NULL }; DECLARE_MODULE(pqb_rig, rig_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_VERSION(pqb_rig, 1); |