# DF-5752 — netbt hands rx mbufs to netisr_queue() (embedded netmsg) and then frees them while still port-queued

**File:** `sys/netbt/hci_unit.c` (pass 2)
**Trigger surface:** `hci_input_event` / `hci_input_acl` / `hci_input_sco` / `hci_complete_sco`

## Build (inside the guest, as root)

```
cd /root/hciuu
sh build.sh          # copies VERBATIM production netbt sources (md5s recorded), builds hci_unit_harness.ko
```

## Run (inside the guest, as root)

```
kldload /root/hciuu/hci_unit_harness.ko
sysctl hw.hciu_setup=1        # attach fake unit + ACL link (hci_attach, production)
sysctl hw.hciu_fire=2         # BASELINE: 8 filler events; prints dangling-netmsg proof lines
sysctl hw.hciu_fire=1         # FIRE: same burst + mid-drain re-injection from netisr context
```

## Expected

* `fire=2` (baseline): the guest **survives**, but prints up to 16
  `DF-5752 PROOF: mbuf 0x... was already freed by hci_event() ... still had
  MSGF_QUEUED set` lines — the netisr port queue is holding messages whose
  backing mbufs are already freed (`dangling_observations=21` observed).
* `fire=1` (fire): **kernel panic**

```
panic: netisr worker held 1 spinlock. link elm 0xfffff80117da8428 next->prev != elm
lwkt_spin_getport() at lwkt_spin_getport+0x134
netmsg_service_loop() at netmsg_service_loop+0xaf
```

The re-injection allocations reuse the just-freed chunks (objcache LIFO)
while their netmsgs are still linked on the netisr thread's port queue;
`netisr_queue()` re-initialises a live message node and `TAILQ_INSERT_TAIL`s
the **same node a second time** — the port queue is corrupted and
`_lwkt_pullmsg()`'s `TAILQ_REMOVE` trips the queue-consistency check.

## Fix check

Apply `fix.diff` to `/usr/src/sys/netbt` (hci_unit.c: 4x
`netisr_queue(NETISR_BLUETOOTH, m)` → `schednetisr(NETISR_BLUETOOTH)`;
bt_input.c: `btintr` must `lwkt_replymsg` the kick message — same contract
as `ngintr()`), rebuild the module, reboot, re-run `fire=2` and `fire=1`:
`dangling_observations=0` and **no panic** (see `fix_run.log`).
