{
  "finding_id": "DF-5754",
  "status": "reproduced",
  "verdict": "Reproduced as a use-after-free on the unit object: hci_detach() (hci_unit.c:120-130) removes the unit from hci_unit_list under a crit_enter() that only defers interrupts on the detaching CPU and kfree()s it, while btintr() (bt_input.c:37) walks the same list and hci_intr()s per-packet loop re-acquires lockmgr(&unit->hci_devlock, LK_EXCLUSIVE) (hci_unit.c:335) on the netisr thread - under rx load the freed units lock is then operated through freed/reused memory. Baseline rig (300 rounds of hci_attach + flood + hci_detach) faulted in round 0/1: Fatal trap 12, read at 0x90, Stopped at lockmgr_exclusive+0x5d, current process Idle. Distinct from DF-0683 (queue/list mutation inside hci_disable racing hci_intr): the faulting object here is the unit itself, freed only by hci_detach. hci_unit_lookup() (hci_unit.c:265-279) likewise returns raw unreferenced pointers retained by sockets/upper layers. Fix validated: spinlock-guarded list + hci_refcnt taken by the btintr snapshot walker + hci_detach waiting the count before kfree -> all 300 flood-detach rounds clean.",
  "reproduced": true,
  "impact": "panic",
  "confidence": "certain",
  "exploit_chain": "controller detach (USB hot-unplug / kldunload of the driver) while rx traffic is in flight -> kfree(unit) underneath btintr()/hci_intr() (or an in-flight hci_input_event) -> lockmgr operates on freed M_BLUETOOTH chunk; chunk reuse (next hci_attach / link alloc, same zone) turns the stale lock pointer into arbitrary-address read/write in lockmgr; escalation requires root-or-physical detach trigger, so the practical impact is a privileged-context memory-corruption/DoS, plus the unrefcounted hci_unit_lookup pointers dangling in sockets after any detach",
  "evidence": "panic.txt / run.log: Fatal trap 12 ... fault virtual address = 0x90, supervisor read ... current process = Idle ... Stopped at lockmgr_exclusive+0x5d: orl (%r14),%esi after round 0 survived; fix_run.log: post-fix rounds through 280 and all rounds done without a hit with guest alive",
  "kernel_refs": [
    "sys/netbt/hci_unit.c:123",
    "sys/netbt/hci_unit.c:126",
    "sys/netbt/hci_unit.c:129",
    "sys/netbt/hci_unit.c:335",
    "sys/netbt/hci_unit.c:265",
    "sys/netbt/bt_input.c:37"
  ],
  "poc_changes": "rig attaches/detaches production units in a loop while a kthread floods production hci_input_event() (the driver-rx stand-in); the fake drivers disable() stops and drains the injector exactly as a real controller driver must (increment-before-check discipline after a rig TOCTOU initially let one call slip past the drain - documented in VERDICT.md); fix-check mode uses memo-free filler packets to keep the known DF-0683 (hci_disable drain race) separable from this unit-lifetime race",
  "attempts": 4,
  "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": 600,
  "guest_dirty": false,
  "build_cmd": "cd /root/hciuu && sh build.sh",
  "run_cmd": "kldload ./hci_unit_harness.ko; sysctl hw.hciu_setup=1; sysctl hw.hciud_fire=300",
  "code_hash": "d6b73fd72e85527a98201eb9da96d1256e9d1bda0ddd017d55267f708cf018dc",
  "notes": "Trigger requires driver detach (root via kldunload, or physical hot-unplug), hence Medium severity despite the clean UAF. An earlier fix-check iteration survived 100+ rounds free of the unit-lifetime signature and then tripped the separate known DF-0683 (hci_disables unlocked drain) - different function/mechanism; final shipped rig avoids that surface and completed 300 rounds with no incident. The DF-5752 fix (schednetisr) and this fix both touch btintr(); the shipped per-finding diffs apply independently to pristine sources but must be merged (fix5752s replymsg line + fix5754s snapshot walk) when combined, as done in the validated combined tree.",
  "recommended_fix": "Guard hci_unit_list with a spinlock, give struct hci_unit a reference count acquired by the btintr() snapshot walker, and make hci_detach() wait the count out before kfree()",
  "fix_status": "fixed",
  "fix_baseline_reproduced": true,
  "fix_patched_reproduced": false,
  "fix_verdict": "Applied fix.diff (hci.h hci_refcnt member + prototypes; hci_unit.c spinlock-guarded attach/detach with refcnt wait + hci_unit_ref/hci_unit_rele; bt_input.c snapshot walk under the spinlock, merged with the DF-5752 replymsg line), rebuilt, rebooted, re-ran hciud_fire=300: rounds 0..280 all survived, all rounds done without a hit, guest alive and responsive.",
  "fix_evidence": "findings/poc/DF-5754/fix_run.log: round 280 survived and all rounds done without a hit with the guest still up (post-fix); contrast panic.txt (lockmgr_exclusive UAF at round <=1 pre-fix)"
}