# DF-3476 — VERDICT

**status: untested / impact: dos (if triggered; not executable on guest) / confidence: likely**

## Why untested

The deadlock needs an RFCOMM DLC with an armed 60 s DLC timer, which
requires an established L2CAP session over real Bluetooth hardware
(`ng_hci`/HCI connection). The audit guest has no Bluetooth hardware;
`ng_btsocket_rfcomm_connect()` fails in `session_create`/`session_connect`
(L2CAP cannot route to any remote bdaddr without an HCI node) and no DLC
timer is ever armed. Verified on the guest that
`socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM)` attaches, but
connect/listen paths cannot reach DLC CONFIGURING/CONNECTING/DISCONNECTING
states without hardware. No other userland-reachable lever arms
`pcb->timo`.

## Why the bug is real (source trace)

1. `ng_btsocket_rfcomm_timeout()` arms
   `callout_reset(&pcb->timo, ..., ng_btsocket_rfcomm_process_timeout, pcb)`
   (:3522-3523).
2. On expiry, softclock sets `CALLOUT_INPROG` **before** calling the
   callback and clears it only **after** it returns
   (sys/kern/kern_timeout.c:567, :630, :644).
3. `ng_btsocket_rfcomm_process_timeout()` first does a **blocking**
   `lockmgr(&pcb->pcb_lock, LK_EXCLUSIVE)` (:3554).
4. Concurrent pcb_lock holders call `ng_btsocket_rfcomm_untimeout(pcb)` →
   `callout_stop(&pcb->timo)` **while still holding pcb_lock** — five
   sites: :769 (detach), :835 (disconnect), :2112 (receive_sabm), :2273
   (receive_ua), :3404 (pcb_kill — also holds session_lock).
5. `callout_stop()` = `_callout_cancel_or_stop(cc, CALLOUT_STOP, 1)`
   (kern_timeout.c:1091) — the `1` makes it synchronous: with INPROG set
   it `ssleep("costp")`s until softclock processes the STOP, which happens
   only after the callback returns (kern_timeout.c:643-647 + update path).

   → thread Y holds pcb_lock and sleeps waiting for the callback; the
   callback (thread X, softclock) sleeps waiting for pcb_lock. Circular,
   non-interruptible, permanent.

6. The window is real whenever the timer fires during a pcb_lock critical
   section that reaches untimeout. `pcb_kill`'s critical sections include
   L2CAP sends/msc machinery; `receive_ua`/`receive_sabm` are directly
   timeable by a remote Bluetooth peer; `disconnect`/`detach` are timeable
   by the local user. Note the TIMO flag is still set during the window
   (the callback clears it only *after* acquiring the lock at :3560), so
   every pcb_kill/disconnect on a timer-armed DLC takes the untimeout
   branch — the inversion is not masked by flags.

Consequence once triggered: triggering thread (unkillable D-state —
`lksleep(..., 0, "rf_det", 0)` :785 has no PCATCH), the RFCOMM task thread
(holds sessions_lock paths → whole bluetooth RFCOMM subsystem stops), and
one CPU's softclock thread all wedge permanently; system-wide timer service
on that CPU degrades. Reboot required.

Severity: Medium — remote-triggerable on Bluetooth-equipped systems with an
active RFCOMM peer (unusual config for servers, default-relevant for
desktops/laptops with BT), permanent DoS.

## Fix

See fix.diff: keep the callback from blocking on pcb_lock (flag-only
callback + state processing moved into the already-locked task path), which
breaks the inversion at its root; alternatively hold no pcb_lock across
untimeout at the five call sites (bigger, more invasive diff).
Not kernel-build-validated on the guest (finding is untested end-to-end —
no hardware — so a green build would prove little; the diff mirrors the
task-side locking already used at :1805/:1817/:1854).

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
