{
 "finding_id": "DF-2870",
 "status": "reproduced",
 "reproduced": 1,
 "impact": "dos",
 "confidence": "certain",
 "verdict": "100% deterministic permanent kernel deadlock. taskqueue_free() called while any task of the queue is in-flight never returns: taskqueue_terminate()'s wakeup(tq) (subr_taskqueue.c:144, fired after TQ_UNLOCK) lands while the worker executes ta_func (not on the sleep queue) and is lost; when ta_func returns, taskqueue_thread_loop() parks in TQ_SLEEP(tq,tq) at :619 without re-checking TQ_FLAGS_ACTIVE, and the terminator is already asleep on tq_threads at :146 - both sleep forever with no timeout and no remaining waker (post-free enqueues return EPIPE at :199, so taskqueue_thread_enqueue never fires). Guest proof: harness blocks ta_func on a flag, calls taskqueue_free, releases the func from a third thread; RESULT: DEADLOCK CONFIRMED - free stuck 10+s, ps -axH shows tqdeadctrl in wchan 'taskqueue_terminate' and the worker in 'tqthr' permanently. Every in-tree taskqueue_free() consumer (wlan ieee80211.c:464 plus ~15 drivers) wedges its calling thread this way when freeing a still-active queue (detach under load -> local DoS). fix.diff (re-check TQ_FLAGS_ACTIVE under the held spinlock between run and sleep) validated: 'taskqueue_free RETURNED (not reproduced)', no wedged threads, module unloads cleanly.",
 "exploit_chain": "",
 "evidence": [
  "run.log: 'RESULT: DEADLOCK CONFIRMED - taskqueue_free() stuck for 10+ seconds'",
  "run.log ps: 'B2 taskqueu tqdeadctrl' + 'B4 tqthr tqdead' (both parked forever)",
  "run.fixed.log: 'taskqueue_free RETURNED (not reproduced)' + NO-TQDEAD-THREADS + clean unload"
 ],
 "kernel_refs": [
  "sys/kern/subr_taskqueue.c:141",
  "sys/kern/subr_taskqueue.c:144",
  "sys/kern/subr_taskqueue.c:146",
  "sys/kern/subr_taskqueue.c:153",
  "sys/kern/subr_taskqueue.c:155",
  "sys/kern/subr_taskqueue.c:617",
  "sys/kern/subr_taskqueue.c:619"
 ],
 "poc_changes": "initial DECLARE_MODULE build GPFs this guest's kldload (loader quirk; DEV_MODULE used instead); harness watchdog originally mis-initialized (hit_thr=-1 made the loop a no-op) - fixed before the recorded run",
 "attempts": 2,
 "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
 "runtime_sec": 6900,
 "guest_dirty": 0,
 "build_cmd": "cd /root/poc/tqdead && make -m /usr/share/mk SYSDIR=/usr/src/sys (see build.sh)",
 "run_cmd": "kldload /root/poc/tqdead/tqdead.ko; sleep 15; dmesg | grep tqdead; ps -axH -o stat,wchan,comm | grep tqdead (see run.sh)",
 "code_hash": "8b9629c14a1e16e6611285be85f51d54932bef8ab7bfe5151cdd7cf28ac8d638",
 "fix_status": "fixed",
 "fix_kernel_uname": "DragonFly 6.5-DEVELOPMENT #1: Wed Sep  2 17:34:00 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC",
 "fix_baseline_reproduced": 1,
 "fix_patched_reproduced": 0,
 "fix_verdict": "Combined fix.diff (per-task TASKQ_RUNNING under TQ_LOCK + wakeup after unlock in taskqueue_run; ACTIVE re-check before worker park; tq_callouts wait in taskqueue_free; timeout_func freer wakeup) built as kernel #1 Wed Sep 2 17:34:00. Baseline bad behavior GONE on every PoC: DF-2869 cancel=EBUSY + drain blocks + no UAF/panic + clean unload; DF-2870 taskqueue_free returns, no wedged threads; DF-2872 timeout task no longer runs after free; DF-2873 hammer does not lose a wakeup (bounded negative). Guest left clean via vm.sh reset with-src.",
 "fix_evidence": [
  "run.fixed.log"
 ],
 "notes": "wedged threads are permanent - guest reset performed after evidence capture",
 "recommended_fix": "Re-check TQ_FLAGS_ACTIVE under the queue spinlock between taskqueue_run() and TQ_SLEEP in taskqueue_thread_loop() so a wakeup delivered while ta_func runs cannot be lost."
}