# DF-3516 VERDICT — REPRODUCED (panic), fix validated

## Reproduced? YES — deterministic kernel panic, 4 syscalls, no race.

Guest: DragonFly 6.5-DEVELOPMENT #0 (X86_64_GENERIC, stock INVARIANTS kernel),
netgraph.ko + ng_socket.ko from /boot/kernel. PoC run as root.

## The chain, line by line

1. `socket(AF_NETGRAPH, SOCK_DGRAM, NG_CONTROL=2)` → `ngc_attach`
   (sys/netgraph/socket/ng_socket.c:165-182). Root-gated at :172
   (`caps_priv_check(SYSCAP_RESTRICTEDROOT|__SYSCAP_NULLCRED)`) →
   `ng_attach_cntl` (:476-503): creates pcb + ngsock, `pcbp->sockdata = privdata`,
   `sockdata->refs = 1`.
2. `bind(s, {"shutme"})` → `ngc_bind` → `ng_bind` (:709-723): names the node.
3. `sendto(s, NGM_SHUTDOWN, ..., "shutme:")` → `ngc_send` (:201-273):
   `pcbp->sockdata->node` (:257) is still valid here; `ng_send_msg`
   (sys/netgraph/netgraph/ng_base.c:1208-1233) resolves the path and runs
   `ng_generic_msg` → `case NGM_SHUTDOWN: ng_rmnode(here)` (ng_base.c:1256-1258).
   `ng_rmnode` (:425-449) sets `NG_INVALID` and calls the type shutdown
   `ngs_rmnode` (ng_socket.c:910-935):
   - `dpcbp`/`pcbp` non-NULL → `soisdisconnected(pcbp->ng_socket)`,
     `pcbp->sockdata = NULL`, `sockdata->refs--`
   - `node->private = NULL`, `ng_unref(node)`, `kfree(sockdata)`
   — the control socket **stays open** with `so_pcb = pcbp` (nothing clears it;
   only `ngc_detach` does). sendto returns 52 (bytes sent).
4. `connect(s, {"shutme:link0"})` → `soconnect` (sys/kern/uipc_socket.c:685-713):
   no `SS_ISCONNECTED`/`SS_ISDISCONNECTED` rejection for PR_ATOMIC|PR_ADDR sockets
   → `so_pru_connect` → `ngc_connect` (:291-303) → `ng_connect_cntl`
   (:680-703): `sockdata = pcbp->sockdata` == NULL; `ng_path_parse("shutme:link0")`
   returns node+hook (both non-NULL, so the :690 guard passes); :694
   `ng_findname(sockdata->node, node)` evaluates `sockdata->node` —
   `struct ngsock.node` is the first member (offset 0) → **read of vaddr 0x0**.

Observed (dfbsd-qemu/boot.log, captured in panic.txt / run.log):

```
Fatal trap 12: page fault while in kernel mode
fault virtual address    = 0x0
fault code               = supervisor read data, page not present
instruction pointer      = 0x8:0xffffffff8263e9d2
Stopped at      ngc_connect+0x52:       movq    (%r13),%rdi
```

`movq (%r13),%rdi` is exactly the load of `sockdata->node` (r13 = NULL sockdata)
whose result feeds `ng_findname`'s first argument. Guest died; ssh hung; vm status
"down" right after. Classification: reproduced / impact=panic.

## Why not uid0?
NULL-deref read at a fixed offset 0 — a denial-of-service primitive, not a
write. No escalation path.

## PoC corrections vs the seed I wrote myself
- NG_CONTROL is 2 on DragonFly (guessed 1) — use
  `<netgraph/socket/ng_socket.h>`.
- Step 2 originally a second `sendto` — blocked: `soisdisconnected()` makes
  `sosend` fail EPIPE/SIGPIPE *before* `ngc_send` runs (observed EXIT=141).
  `connect()` is the route: `soconnect` has no disconnected-state gate.
- Connect address must be `node:hook` (both parts) or `ng_path_parse` returns
  hook==NULL → early EINVAL (:690) before the deref. `shutme:link0` works.

## Fix validation — FIXED (module-level)
fix.diff adds the missing NULL checks (mirroring ngd_send:357/ng_bind:714):
- `ngc_send`: `if (pcbp->sockdata == NULL) { error = ENOTCONN; goto release; }`
- `ng_connect_cntl`: `if (sockdata == NULL) { TRAP_ERROR; return (ENOTCONN); }`

Applied in guest /usr/src (repo sys/ untouched), module rebuilt
(`sys/netgraph/socket` make), loaded over stock. Same PoC:
`connect returned -1 errno=57 (ENOTCONN)`, no panic, guest alive, and the
NGM_SHUTDOWN good-path send still works (returns 52) — no regression.

## Kernel refs
- sys/netgraph/socket/ng_socket.c:257 (ngc_send bare deref)
- sys/netgraph/socket/ng_socket.c:694,701 (ng_connect_cntl bare deref)
- sys/netgraph/socket/ng_socket.c:910-935 (ngs_rmnode NULLs sockdata, frees ngsock,
  leaves so_pcb set; soisdisconnected)
- sys/netgraph/socket/ng_socket.c:357,714,444 (the three sibling paths that DO check)
- sys/netgraph/netgraph/ng_base.c:1256-1258 (NGM_SHUTDOWN → ng_rmnode)
- sys/kern/uipc_socket.c:685-713 (soconnect — no SS_ISDISCONNECTED gate)

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