# DF-3479 — ng_btsocket_l2cap_attach() misses the "implied pcb ref" (soreference): KKASSERT panic in sonewconn on every inbound L2CAP connection; socket refcount runs one low on non-INVARIANTS kernels

## What

DragonFly's `sonewconn_faddr()` (sys/kern/uipc_socket2.c) attaches a new pcb
to the freshly allocated child socket by direct-calling the protocol's
`pru_attach` and then asserts the socket reference accounting:

```c
	so_pru_attach_direct(so, 0, &ai);
	KKASSERT(((so->so_proto->pr_flags & PR_ASYNC_RCVD) == 0 &&
	    so->so_refs == 2) || ...);       /* uipc_socket2.c:398-404 */
					   /* "attach + our base ref" */
```

PCB-based protocols take that second reference when they attach (e.g.
`in_pcballoc()` calls `soreference(so)`, sys/netinet/in_pcb.c:295).
`ng_btsocket_l2cap_attach()`
(sys/netgraph7/bluetooth/socket/ng_btsocket_l2cap.c:1912-2030) links
`so->so_pcb`/`pcb->so` but never calls `soreference()`, so on every inbound
connection (`ng_btsocket_l2cap_process_l2ca_con_ind()` :593 ->
`sonewconn(pcb->so, 0)`) the assertion fires on INVARIANTS kernels:

    panic: assertion "... so->so_refs == 2 ..." failed in sonewconn_faddr
        at /usr/src/sys/kern/uipc_socket2.c:404
    sonewconn_faddr() <- ng_btsocket_l2cap_input() <- taskqueue_run() <- ithread_handler()

On non-INVARIANTS (production) kernels the assertion is compiled out and the
child socket's refcount simply runs one reference lower than the socket-layer
contract — the pcb (still registered in `ng_btsocket_l2cap_sockets`) can
outlive the freed socket (`pcb->so` dangling), a latent use-after-free
whenever an accepted L2CAP socket is closed.

## Threat

Remote-triggerable: any Bluetooth peer that opens a connection to a PSM a
local socket is listening on (the ordinary "connect to SDP/RFCOMM service"
flow) makes the kernel panic (INVARIANTS builds). Locally reproducible with
the DF-3480 harness (fake ng_l2cap lower layer, no BT hardware needed):
deliver NGM_L2CAP_L2CA_CON_IND for a bound/listening PSM.

## Reproduce

Guest with netgraph7 bluetooth stack loaded, btsbx.ko harness (see ../DF-3480)
connected to the `btsock_l2c` node; a listener bound to PSM 0x1234:

    sysctl -w debug.btsbx_cmd=1   # setup hooks + hook_info
    sysctl -w debug.btsbx_cmd=2   # con_ind -> panic (baseline)

Baseline panic.txt captured. With fix.diff applied (adds the missing
`soreference(so)` — plus `#include <sys/socketvar2.h>`), the same command
completes: the harness drove the full CON/CFG handshake to OPEN, accept()
returned a child socket, and 16M+ data packets were transferred through it.

## Fix (validated)

```diff
--- a/sys/netgraph7/bluetooth/socket/ng_btsocket_l2cap.c
+++ b/sys/netgraph7/bluetooth/socket/ng_btsocket_l2cap.c
@@ -1960,6 +1960,7 @@
 	so->so_pcb = (caddr_t) pcb;
 	pcb->so = so;
 	pcb->state = NG_BTSOCKET_L2CAP_CLOSED;
+	soreference(so);	/* implied pcb ref (sonewconn expects so_refs==2) */
```

(note: ng_btsocket_l2cap_raw / rfcomm / hci_raw attach paths should be
audited for the same omission — separate files.)
