# DF-5747 — VERDICT

**Status: reproduced** (rig, verbatim production code) · **Impact: none (semantic
bug / API hardening)** · **Confidence: certain**

## What was run

KLD rig `rfcomm_up5747.ko`: `sys/netbt/rfcomm_session.c`, `rfcomm_dlc.c`,
`rfcomm_upper.c` copied **unmodified** from the guest's `/usr/src/sys/netbt`
(md5 `3528f6f33426dbb23216d88859ec8098` for rfcomm_upper.c, identical to the
audit tree; recorded in `setup.log`), compiled against a stub L2CAP layer
(`l2cap_stub.c`). Trigger `sysctl hw.df5747_fire=1` calls only production
entry points: `rfcomm_attach` → `rfcomm_rcvd` → `rfcomm_bind` →
`rfcomm_listen` → **`rfcomm_bind` (again, state=LISTEN)** →
`rfcomm_disconnect` → `rfcomm_detach`.

## Observed (baseline, `run.log`)

```
[DF-5747] bind #1 (state CLOSED)          -> 0 (expect 0)
[DF-5747] listen()                        -> 0; state=8 (8=LISTEN), channel=3
[DF-5747] bind #2 (state LISTEN)          -> 0  (BUG: live listener mutated; upstream returns EINVAL)
[DF-5747] listener now advertises: channel=5 psm=0x1001 state=8
[DF-5747] disconnect() on LISTEN dlc      -> 22 (expect EINVAL)
[DF-5747] detach() complete, dlc freed, listen session freed
```

The production `rfcomm_bind()` (rfcomm_upper.c:103-109) accepted a re-bind of
a **live listening DLC**, mutating its advertised channel (3 → 5) and PSM
(0x0003 → 0x1001) in place. NetBSD's `rfcomm_bind_pcb()` returns `EINVAL`
for exactly this call (state check added upstream, present through
rfcomm_upper.c 1.24, 2024). Teardown afterwards was clean — this is a pure
semantic/state-confusion bug (both `rfcomm_listen()`'s channel allocator at
rfcomm_upper.c:357-373 and `rfcomm_dlc_newconn()`'s best-match at
rfcomm_dlc.c:92-113 consume `rd_laddr.bt_channel`), not memory corruption.

## Why it matters / threat

Unprivileged local user (RFCOMM sockets need no privilege; kernel must have
netbt compiled in). Effect confined to the caller's own sockets: re-routing
or starving inbound-connection matching for a listener, inconsistent
`getsockname` output, listener bookkeeping that no longer matches the
listening session. No cross-privilege path exists (a listener can only be
re-bound by its owner). Hence **Info** severity, defense-in-depth /
upstream-divergence class.

## Fix validation (`fix.diff`, `fix_run.log`, `fix_build.log`)

`patch -p3 < fix.diff` on the verbatim copy adds the upstream state check.
Clean rebuild (`make clean` + `MAKEOBJDIRPREFIX` reset — an intermediate
rebuild without these linked a stale `rfcomm_upper.o` from a second objdir
and showed old behaviour; disassembly of the clean rebuild shows
`cmpw $0x0,0xc(%rdi)` / `mov $0x16,%eax` = the EINVAL path, see
`fix_build.log`). Same trigger:

```
[DF-5747] bind #2 (state LISTEN)          -> 22  (EINVAL - fixed)
[DF-5747] listener now advertises: channel=3 psm=0x0003 state=8
```

Bad behaviour gone; `bind #1` (CLOSED state) still returns 0; teardown
clean. `fix_status: fixed`.

## Bottom line

Real, upstream-confirmed API-validation gap in `rfcomm_bind()`; demonstrated
on the guest with verbatim production code; one-line fix validated.

## 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.
