DF-2969 / dfv_2969.c
/* * dfv_2969.c — suspend_kproc lifetime protocol violation. * dfv.s2969=1: spawn kthread A that immediately kthread_exit()s, then * call suspend_kproc(tdA, 0). The target is gone: wakeup()/tsleep() use * a stale identity and the loop reads/writes td_mpflags on a parked * (freed) struct thread -> unkillable sleeper holding kpsus_token. * dfv.s2969x=1: second suspend_kproc on a LIVE kthread -> blocks forever * acquiring kpsus_token (proves the wedge extends system-wide). */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/module.h> #include <sys/sysctl.h> #include <sys/kthread.h> #include <sys/thread.h> #include <sys/thread2.h> static struct thread *dead_td; static struct thread *live_thr; static void exiter_thread(void *arg) { kthread_exit(); } static void live_thread(void *arg) { int i = 0; for (;;) tsleep(&i, 0, "dfvlive", hz); } static int dfv_s2969(SYSCTL_HANDLER_ARGS) { int error, go = 0; error = sysctl_handle_int(oidp, &go, 0, req); if (error || req->newptr == NULL) return (error); if (go != 1) return (0); kprintf("DF2969: creating kthread that immediately exits...\n"); kthread_create(exiter_thread, NULL, &dead_td, "dfvdead"); tsleep(&go, 0, "dfvw", hz); /* let it exit and park */ kprintf("DF2969: target td=%p exited (parked); calling " "suspend_kproc(td, 0) on it now\n", dead_td); kprintf("DF2969: entering suspend_kproc...\n"); suspend_kproc(dead_td, 0); /* expected: never returns */ kprintf("DF2969: suspend_kproc returned?!\n"); return (0); } static int dfv_s2969x(SYSCTL_HANDLER_ARGS) { int error, go = 0; error = sysctl_handle_int(oidp, &go, 0, req); if (error || req->newptr == NULL) return (error); if (go != 1) return (0); if (live_thr == NULL) kthread_create(live_thread, NULL, &live_thr, "dfvlive"); kprintf("DF2969: calling suspend_kproc on LIVE kthread %p -> should " "wedge on kpsus_token held by the stuck suspender\n", live_thr); suspend_kproc(live_thr, 1); kprintf("DF2969: live suspend_kproc returned (kpsus_token not held?)\n"); return (0); } static SYSCTL_NODE(, OID_AUTO, dfv69, CTLFLAG_RW, 0, "DF 2969 rig"); SYSCTL_PROC(_dfv69, OID_AUTO, s2969, CTLTYPE_INT|CTLFLAG_RW, 0, 0, dfv_s2969, "I", "suspend dead kthread"); SYSCTL_PROC(_dfv69, OID_AUTO, s2969x, CTLTYPE_INT|CTLFLAG_RW, 0, 0, dfv_s2969x, "I", "suspend live kthread (wedge probe)"); static int dfv69_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: kprintf("dfv_2969 loaded\n"); return (0); case MOD_UNLOAD: return (EBUSY); /* threads may be wedged */ } return (EOPNOTSUPP); } static moduledata_t dfv69_mod = { "dfv_2969", dfv69_modevent, 0 }; DECLARE_MODULE(dfv_2969, dfv69_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); |