# DF-2572 — ng_device global SLIST race (UAF / NULL-deref) — PoC

## Finding
`sys/netgraph/ng_device.c` keeps a single global `ngd_softc.head` SLIST of
`ngd_connection` that is **mutated** by `ng_device_newhook`
(`SLIST_INSERT_HEAD`, line 309) and `ng_device_disconnect`
(`kfree(readq):407`, `destroy_dev:409`, `SLIST_REMOVE:411`) and **traversed**
(unlocked `SLIST_FOREACH`) by `ng_device_rcvdata` (:345), `ngdioctl` (:464),
`ngdread` (:518), `ngdwrite` (:572), `ngdpoll` (:617). No mutex, no spl, no
serialization anywhere in the file. Two threads racing disconnect against a
reader → UAF on the freed `readq` (10 KiB `M_DEVBUF`) / NULL-deref / list
corruption.

## Verdict: NOT REPRODUCED — unreachable dead code (false positive)

The unsynchronized-list bug is **real in the source text**, but the cited file
is **orphaned dead code**:
- not in `sys/conf/files` (only `netgraph7/ng_device.c` is, at `:1699`),
- not in `sys/config/X86_64_GENERIC`,
- **cannot compile** (the `struct cdevsw`/`d_*_t`/`cdevsw_add`/`make_dev` API
  it uses was removed from DragonFly; 19+ compile errors),
- not in the running kernel (`nm` count = 0), no module on disk, `kldload`
  fails, no `/dev/ngd*` nodes.

The maintained `sys/netgraph7/ng_device.c` does **not** have the bug: it has no
global SLIST at all (per-node `ngd_private` + direct `dev->si_drv1` pointer),
uses proper mutexes (`mtx_init` 171-172, `mtx_lock` throughout), and gets the
disconnect ordering right (`destroy_dev` before `kfree`). Even if the dead file
were live, `/dev/ngdN` is mode `0600` (root-only) → root→kernel, not an
unprivileged escalation.

This is the same dead-code conclusion the sibling finding **DF-2571** reached
for the same file.

## Reproduce
```sh
./build.sh && ./run.sh
```
On the default kernel: `/dev/ngd0` does not exist, `ng_device` is not loaded,
no effect. (The trigger would race a reader against hook create/disconnect if
the module were built and loaded as root.)

## Defense-in-depth fix
`fix.diff` adds a `struct spinlock ngd_lock` to `ngd_softc`, initializes it in
`ng_device_init`, and wraps every `SLIST` mutation/traversal; it also corrects
`ng_device_disconnect` to `destroy_dev`-before-`kfree` (matching the maintained
version). The real resolution is to **delete the orphaned file**
(`netgraph7/ng_device.c` fully supersedes it). `fix_status: not_testable` (no
kernel image to build the fix into; `git apply --check` rc=0).

See `VERDICT.md` for the full reachability analysis.
