# DF-3478 — VERDICT

**status: reproduced / impact: panic (permanent, unprivileged local DoS) / confidence: certain**

## How it was reproduced

Guest: DragonFly 6.5-DEVELOPMENT #0 (X86_64_GENERIC, INVARIANTS),
netgraph7 module stack loaded (netgraph.ko, ng_bluetooth.ko, ng_btsocket.ko).

`step_listen` (see step_listen.c) performs exactly three operations:

```
socket(PF_BLUETOOTH=33, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP=135);
bind(..., {psm=0x1234, bdaddr=ANY});
listen(fd, 4);
```

Serial console (serial console writes survive the panic):

```
STEP: start
STEP: socket done
STEP: bind done
DOUBLE FAULT
Fatal double fault
rip = 0xffffffff806c2b4d
panic: double fault
```

3/3 reproductions across two independent boots (the third occurrence was the
DF-3480 harness driver, which panics identically at its listen() call).
`uid=1001` can perform all three syscalls (DF-3477 shows bind needs no
privilege; socket/listen never check privilege either) — the panic is
unprivileged whenever the bluetooth socket module is loaded.

## Root cause (line-accurate)

`ng_btsocket_l2cap_listen()` — the pru_listen netmsg handler executed on the
netisr cpu0 protocol thread — calls DragonFly's `solisten()`:

- sys/netgraph7/bluetooth/socket/ng_btsocket_l2cap.c:2460 — `solisten(so, backlog, td);`
- sys/kern/uipc_socket.c:330-344 — `solisten()` sets SO_ACCEPTCONN/qlimit and
  ends with `return so_pru_listen(so, td);` (:343)
- sys/kern/uipc_msg.c:341-352 — `so_pru_listen()` does
  `lwkt_domsg(so->so_port, ...)`; for this family `so_port = netisr_cpuport(0)`
  (no `pr_initport` in the protosw; sys/kern/uipc_socket.c:259).

So the handler re-dispatches itself; the domsg is executed inline on the same
protocol thread, recursing without bound:

```
kern_listen -> solisten -> so_pru_listen -> [netisr0] ng_btsocket_l2cap_listen
   -> solisten -> so_pru_listen -> [inline] ng_btsocket_l2cap_listen -> ...
```

until the kernel stack overflows; the deepest frame is always the
`callq lwkt_domsg` inside `so_pru_listen` (rip 0xffffffff806c2b4d =
`so_pru_listen+0x4d`, verified against the guest's /boot/kernel/kernel),
whose return-address push lands on an unmapped page → CPU double fault →
`panic: double fault` → DDB, system dead until reboot.

This is a porting defect: FreeBSD's `solisten()` does not re-dispatch
pru_listen, so the 2008-era ng7 port kept a call that is recursively
self-invoking under DragonFly's message-based socket layer.

## Fix validation

fix.diff removes the `solisten()` call (and the now-unused td/backlog
variables). Applied to the guest's /usr/src copy, ng_btsocket.ko rebuilt and
reloaded:

```
STEP: start
STEP: socket done
STEP: bind done
STEP: listen done
STEP: ALL OK          (RC=0)
```

and the listener thereafter accepts real connections (used by the DF-3479 /
DF-3480 harness, which drives an inbound L2CAP connection to OPEN and
transfers 16M+ packets). Baseline panics; patched completes — fix validated.

## Cross-references

- Same defect in the RFCOMM twin: sys/netgraph7/bluetooth/socket/
  ng_btsocket_rfcomm.c:974 (its own listener) and :1427 (session's L2CAP
  listener — reaches the l2cap handler's recursion from the rfcomm task
  thread). Different file; flagged for that file's audit.

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