DF-2676 / df2676.c
/* * DF-2676 trigger (kernel module): deterministically drive vm_page_grab() * into its "page exists but busy + caller did not pass VM_ALLOC_RETRY" * error path (sys/vm/vm_page.c:3838-3844) and prove the subsequent * NULL-pointer dereference at sys/vm/vm_page.c:3882 (m->valid). * * Two kernel threads: * holder - allocates nothing; just wakes the pre-busied page after 250ms * grabber - calls vm_page_grab(obj, 0, VM_ALLOC_NORMAL) (NO RETRY) * -> lookup_busy_try error -> vm_page_sleep_busy (sleeps once, * holder's wakeup releases it) -> m = NULL; break; * -> "if (m->valid == 0)" dereferences NULL -> trap 12 panic. * * NOTE: with only in-tree callers this path is currently latent (the only * non-RETRY caller, sysv_shm's prealloc loop, holds the object exclusively * and can never observe a busy page); this module proves the primitive * exactly as any future non-RETRY caller would hit it. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/module.h> #include <sys/thread.h> #include <sys/kthread.h> #include <vm/vm.h> #include <vm/vm_object.h> #include <vm/vm_page.h> #include <vm/vm_page2.h> static vm_object_t df_obj; static vm_page_t df_m; static struct thread *df_th0; static struct thread *df_th1; static struct thread *df_th2; static void df_holder(void *arg) { tsleep(&df_m, 0, "df26h", hz / 4); kprintf("DF-2676: holder: waking busy page %p\n", df_m); vm_page_wakeup(df_m); kthread_exit(); } static void df_grabber(void *arg) { vm_page_t m; tsleep(&df_m, 0, "df26g", hz / 8); kprintf("DF-2676: grabber: calling vm_page_grab(obj, 0, " "VM_ALLOC_NORMAL) - NO VM_ALLOC_RETRY - on busy page\n"); m = vm_page_grab(df_obj, 0, VM_ALLOC_NORMAL); kprintf("DF-2676: grabber: grab returned %p (no panic?!)\n", m); kthread_exit(); } static void df_setup(void *arg) { /* * All object/token work must happen in kernel-thread context * (never in the kldload syscall context, whose tokens are * checked when returning to userland). */ df_obj = vm_object_allocate(OBJT_DEFAULT, 4); vm_object_hold(df_obj); df_m = vm_page_alloc(df_obj, 0, VM_ALLOC_NORMAL | VM_ALLOC_NULL_OK); KKASSERT(df_m != NULL); kprintf("DF-2676: loaded, page %p busied in obj %p\n", df_m, df_obj); kthread_create(df_holder, NULL, &df_th1, "df2676h%d", 0); kthread_create(df_grabber, NULL, &df_th2, "df2676g%d", 0); for (;;) tsleep(&df_obj, 0, "df2676w", hz * 60); } static int df2676_event(struct module *mod, int what, void *arg) { switch (what) { case MOD_LOAD: kthread_create(df_setup, NULL, &df_th0, "df2676s%d", 0); return (0); case MOD_UNLOAD: kprintf("DF-2676: unload (unreachable after panic)\n"); return (0); default: return (0); } } static moduledata_t df2676_mod = { "df2676", df2676_event, NULL }; DECLARE_MODULE(df2676, df2676_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_VERSION(df2676, 1); |