# DF-5764 — l2cap_start() unbalanced lc_pending on hci_acl_send() failure

sys/netbt/l2cap_lower.c:203-204 (pass 2)

## What

`l2cap_start()` raises `chan->lc_pending` **before** the fallible
`hci_acl_send()` call:

```c
IF_DEQUEUE(&chan->lc_txq, m);			/* l2cap_lower.c:195 */
...
chan->lc_pending++;				/* :203 */
return hci_acl_send(m, chan->lc_link, chan);	/* :204 — can FAIL */
```

`hci_acl_send()` has two failure returns that free `m` and create **no**
`l2cap_pdu`: ENETDOWN (`link->hl_state == HCI_LINK_CLOSED`,
hci_link.c:546-549) and ENOMEM (zalloc/m_split, hci_link.c:551-553,
568-570, 592-599). `lc_pending` is only ever decremented for a PDU found
on `link->hl_txq` with `lp_chan == chan` (hci_acl_complete,
hci_link.c:730-742) — a PDU that does not exist. The counter is
permanently inflated for the channel's lifetime, and the leak COMPOUNDS
(every later `l2cap_start()` caller — `l2cap_send()`,
`hci_acl_complete()`, `hci_acl_start()` streaming — burns another
blackholed SDU and raises it again).

Consequences, each gated on `lc_pending == 0`:

1. `l2cap_send()` (l2cap_upper.c:398) never calls `l2cap_start()` again:
   every further `send(2)` silently succeeds into `lc_txq` and nothing is
   ever transmitted — permanent TX blackhole for the connection.
2. `l2cap_disconnect(chan, linger)` (l2cap_upper.c:244) never starts the
   graceful L2CAP disconnect handshake — the channel can only die by
   force-close (`close(2)` → detach).
3. `l2cap_start()`'s own L2CAP_SHUTDOWN flush path (l2cap_lower.c:173-179)
   is unreachable.

## Evidence (findings/poc/DF-5764/)

KLD rig (`l2pw_harness.ko`) compiling the **VERBATIM** production
`l2cap_upper.c`, `l2cap_lower.c` (file under audit), `l2cap_misc.c`,
`l2cap_signal.c` and `hci_link.c` (so the ENETDOWN failure branch under
test is production code) against minimal stubs. Channel/link state is
exactly what production builds: `l2cap_attach()` + `l2cap_cid_alloc()` +
one `hci_acl_open()` reference + `L2CAP_OPEN` on a
`HCI_LINK_CLOSED` link (what `hci_link_alloc()` returns — and the state
left behind by the DF-5737 teardown race window; an ENOMEM under memory
pressure produces the identical imbalance).

Build & run INSIDE the guest as root:

```
cd /root/l2pw && ./build.sh && ./run.sh
```

EXPECTED (unfixed): `hw.l2pw_wedge_proven = 1`, `hw.l2pw_ctl_ok = 1`,
no panic. Round prints go to the console (run.sh dumps them):

```
W1 send#1 ret=50 (ENETDOWN=50) lc_pending=1 link_txq_empty=1 lc_txq_len=0
W1 PASS: pending==1 but NO pdu exists on the link that could ever decrement it
W2 64 more sends: ret==0 all, lc_txq_len=64 (blackholed) lc_pending=1
W3 l2cap_disconnect(linger) ret=0 state=5 (OPEN=5) reqs_empty=1   <- handshake dead
W4 l2cap_start ret=50 lc_txq_len=63 lc_pending=2                   <- leak compounds
```

The CONTROL round (link OPEN + production `hci_acl_complete()`) proves
the healthy `lc_pending` 1→0 round-trip works and that the SHUTDOWN
disconnect fires exactly when `pending` reaches 0 — i.e. the wedge is
caused precisely by the failed-send increment.

## Fix

`fix.diff` (validated): only count the PDU when one was actually created —

```diff
-	chan->lc_pending++;
-	return hci_acl_send(m, chan->lc_link, chan);
+	err = hci_acl_send(m, chan->lc_link, chan);
+	if (err == 0)
+		chan->lc_pending++;
+	return err;
```

Apply to /usr/src inside the guest, rebuild the module (`./build.sh`
re-copies the verbatim source), re-run: `wedge_proven` flips to 0
(failed send leaves `lc_pending == 0`; further sends get honest
`ENETDOWN` per call with an empty queue; `l2cap_disconnect()` closes the
channel), `ctl_ok` stays 1. See `fix_run.log`.

## Impact

Local/remote DoS of an established L2CAP connection's transmit path
(silent data blackhole + stuck graceful-disconnect), triggerable by any
`hci_acl_send()` failure while OPEN — ENOMEM under memory pressure, or
ENETDOWN in the link-teardown window (co-fires with DF-5737's race).
No memory corruption; stuck mbufs are bounded by the socket send buffer
(l2cap_ssend appends the original to `so_snd` before handing the copy
down). Severity Low.
