# DF-3517 — v1 ng_socket: no synchronization at all on ngsock/ngpcb lifetime (DF-3510 v1 twins)

## What
The v1 AF_NETGRAPH socket code (`sys/netgraph/socket/ng_socket.c`) shares
`struct ngsock` (node/datasock/ctlsock/flags/refs) and `struct ngpcb` across
three genuinely concurrent execution contexts with **zero locking**:

- **socket protocol threads**: `ngc_send`/`ngd_send`/`ngc_connect`/`ngc_detach`/
  `ngd_detach` (pru handlers dispatched on each socket's lwkt port);
- **the netgraph netisr thread**: queued control messages / `ngintr`
  (ng_base.c:2045-2108) → `ngs_rcvmsg`/`ngs_disconnect`;
- **any data-sender's thread**: v1 `ng_send_data` dispatches the peer's rcvdata
  **synchronously in the caller's context** (ng_base.c:1678-1697), so
  `ngs_rcvdata` runs on whatever thread sent data into the socket node.
  Control messages are the same: `ng_send_msg` calls `CALL_MSG_HANDLER`
  directly (ng_base.c:1186-1233) — no queue, no INVALID re-check — so
  `ngs_rcvmsg` runs on any sender's thread.

Mutations happen on the socket threads (`ng_detach_common:552-574` NULLs
sockdata, does unlocked `--sockdata->refs`, kfrees the pcbp;
`ng_connect_data:669-671` does unlocked `refs++`) and in `ngs_rmnode:919-933`
which kfrees `sockdata` **unconditionally (not refs-gated)**.

Bare cross-thread reads / TOCTOU windows:
- `ngc_send:257` `pcbp->sockdata->node`
- `ngd_send:357` (NULL check) then `:368`/`:376` `pcbp->sockdata->node` use
- `ngs_rcvmsg:792-793` `node->private` → `sockdata->ctlsock`
- `ngs_rcvdata:847-859` `hook->node->private` → `sockdata->datasock` →
  `pcbp->ng_socket` (pcbp freed concurrently by `ng_detach_common:574`)
- `ngs_disconnect:895-899` `hook->node->private`, `numhooks`

This is the v1 twin of ng7 DF-3510 (which identified the ng7 socket's five
bare-read sites racing mtx-protected teardown). v1 is strictly worse: there is
no mtx anywhere, `ngs_rmnode`'s kfree isn't reference-gated, and v1's only
"protection" is `crit_enter()` in `ng_unref` (ng_base.c:474-484), which does not
stop another CPU. DF-0537 covered only ng7's `ng_connect_data` TOCTOU.

## Impact
- Window A (NULL-deref panic): `ngs_rmnode` NULLs `sockdata`/`datasock`/frees
  between a reader's check and use → read of freed/NULL memory → panic
  (same sink class as DF-3516, but requiring the race).
- Window B (pcb UAF): `ngs_rcvdata` holds `pcbp` across
  `ssb_appendaddr(&so->so_rcv, ...)` (:873-882) while `ng_detach_common`
  kfrees the pcbp (:574) and `sofree`s the socket → use-after-free on the
  socket buffer / socket.
- Window C (refs lost-update): unlocked `refs++`/`--` on different CPUs →
  premature or missed free.

Unprivileged reachability: data sockets have **no** privilege check
(`ngd_attach:310-321`), so an unprivileged holder of a data socket can race
teardown of a root-configured graph (same precondition DF-3510 documented).
The control-message paths need a root-held control socket.

## Why untested
Same judgment as DF-3510: the window is a few instructions wide, teardown must
land inside it, and race-hammering costs seconds per attempt with a panic as
the only positive signal; the deterministic sibling DF-3516 was verified
instead. Filed on the lock-usage asymmetry (all mutating paths vs all reading
paths listed above).

## Fix direction
Give the ngsock a lock (or route all ngsock/ngpcb access through one context):
mirror the ng7 discipline — lock around the loads, take a counted reference on
`sockdata->node` before deref, re-check after; make `ngs_rmnode`'s free
refs-gated. The minimal hardening is to reuse the DF-3516 NULL checks plus an
ngsock mutex around `sockdata`/`ctlsock`/`datasock` read-modify pairs.
