DF-3660 / trig_detach.c
/* * DF-3660 - isp_detach() leaves FC worker kthreads, ldt/gdt callouts and * (with ISP_TARGET_MODE) target threads running against state that * isp_pci_detach() then frees. * * Lifetime chain (all citations are this audit tree): * * isp_attach_chan() isp_freebsd.c:159 * THREAD_CREATE(isp_kthread, fc, &fc->kthread, ...) - per FC channel; * isp_kthread (isp_freebsd.c:4178-4281) is `for (;;) { ... lksleep(fc, * &isp->isp_osinfo.lock, ...); }` - NO exit condition, no is_exiting * flag, nothing ever terminates or reaps it. * * isp_detach() isp_freebsd.c:265-323 * - never tells the kthreads to stop, never wakes them, never waits; * - stops only isp_osinfo.tmo (286-289) - the per-channel fc->ldt / * fc->gdt callouts (isp_freebsd.h:177-178) are left armed; * - frees paths, deregisters buses, cam_sim_free()s the sims. * * isp_pci_detach() isp_pci.c:997-1031 * isp_uninit(); bus_teardown_intr(); lockuninit(&isp->isp_osinfo.lock) * (isp_pci.c:1017); bus_release_resource(regs); kfree(isp->isp_param) * (isp_pci.c:1024); kfree(pc.ptr) (isp_pci.c:1028); the newbus softc * itself is freed after detach returns. * * After that, the still-scheduled kthread/callout/task code executes against * freed memory: * - lksleep on the lockuninit()'d isp_osinfo.lock (UAF lock) * - isp_fc_runstate() -> FCPARAM(isp, chan)->... (UAF read of * the kfree'd isp_param / fc block) * - isp_prt() -> device_get_nameunit(isp->isp_dev) (UAF read) * - mailbox commands -> ISP_READ/ISP_WRITE via bus_tag/bus_handle whose * bus resource was released (isp_pci.c:1022) (wild MMIO) * - isp_ldt/isp_gdt callouts -> taskqueue_enqueue(&fc->ltask/gtask) then * isp_ldt_task/isp_gdt_task touching FCPARAM(isp, chan) (UAF) * * Trigger: any isp(4)-attached device being detached - `kldunload isp` on * modular kernels, devctl detach, or driver reattach - i.e. root, or a * hotplug event on hardware that supports it. * * Upstream FreeBSD *does* reap: isp_detach_chan() does * `wakeup(fc); while (fc->num_threads != 0) mtx_sleep(...);` and isp_detach() * sets isp_osinfo.is_exiting plus callout_drain(&isp_osinfo.tmo) * (verified against FreeBSD HEAD, fetched 2026-09-15). DragonFly's copy has * none of that machinery. * * This guest has no QLogic PCI device, so the path cannot be executed here; * the chain above is source-certain. This file documents the trigger. */ int main(void) { return 0; } |