# DF-2571 — ng_device stack VLA overflow — VERDICT

## Verdict: NOT REPRODUCED (unreachable dead code; impact claim is a false positive)

The VLA anti-pattern the finding describes is **genuinely present** in the cited
source text, but the cited file is **orphaned dead code** that is not compiled
into any shipping DragonFlyBSD kernel or module, and **cannot even compile**
against the current kernel headers. The finding's impact claim — "controllable by
an unprivileged user" — is therefore a **false positive**. The maintained
equivalent (`sys/netgraph7/ng_device.c`) already eliminated the bug.

---

## What the finding claims

`sys/netgraph/ng_device.c` `ngdread`/`ngdwrite` declare a stack VLA whose size
comes directly from user-supplied `uio->uio_resid`:

```c
/* ngdread  — sys/netgraph/ng_device.c:509 */
char buffer[uio->uio_resid+1];

/* ngdwrite — sys/netgraph/ng_device.c:562 */
char buffer[uio->uio_resid];
```

`uio->uio_resid` is a `size_t` taken from the `count` argument to `read()`/`write()`.
The DragonFly lwkt thread stack is 16 KB (`UPAGES(4) * PAGE_SIZE(4096)` =
`LWKT_THREAD_STACK` = 16384, `sys/cpu/x86_64/include/param.h:126`,
`sys/sys/thread.h:472`). A read/write of more than ~16 KB would make the VLA
overrun the stack into the guard page → kernel panic (double fault). **The VLA
pattern is real and would be dangerous IF the code were compiled and reachable.**

## Why it does NOT reproduce — full trace

### (1) The cited file is dead code — no build path

| Build path | Status for `sys/netgraph/ng_device.c` |
|---|---|
| `sys/conf/files` | **No entry.** (The only `ng_device` entry, at `conf/files:1699`, is for `netgraph7/ng_device.c` — a *different* file.) |
| Module Makefile (`sys/netgraph/Makefile`) | **No `device` SUBDIR.** There is no `sys/netgraph/device/` directory. |
| Kernel config (`sys/config/X86_64_GENERIC`) | **No `netgraph7_device` / `ng_device` option.** |
| Compiled into running kernel | **No.** `nm /boot/kernel/kernel.debug \| grep ng_device` = empty. |
| Loadable module on disk | **No.** `kldload ng_device` → "No such file or directory". |

### (2) The cited file cannot even compile

Building `sys/netgraph/ng_device.c` as a kld module (mirroring the
`ng_echo`/`ng_socket` pattern) **fails hard** — the entire `struct cdevsw` /
`d_*_t` / `cdevsw_add` / `make_dev` character-device API it targets was removed
from DragonFly years ago:

```
ng_device.c:111: error: unknown type name 'd_close_t'
ng_device.c:112: error: unknown type name 'd_open_t'
ng_device.c:113: error: unknown type name 'd_read_t'
ng_device.c:114: error: unknown type name 'd_write_t'
ng_device.c:119: error: variable 'ngd_cdevsw' has initializer but incomplete type
ng_device.c:132: error: 'nommap' undeclared here
ng_device.c:133: error: 'nostrategy' undeclared here
ng_device.c:286: warning: implicit declaration of function 'make_dev'
ng_device.c:419: error: 'ngdopen' redeclared as different kind of symbol
ng_device.c:506: error: 'ngdread' redeclared as different kind of symbol
...
Stop.
```

(Full output in `module_build_failure.txt`.) The file is a relic of the
pre-netgraph7 era (FreeBSD 1.1.2.1, 2002) and has not tracked the kernel API.

### (3) The maintained version is already fixed

`sys/netgraph7/ng_device.c` — the file actually referenced in `conf/files`
(as `optional netgraph7_device`) — uses the modern `struct cdev`/`dev_ops` API
and **already eliminated both VLAs**:

```c
/* ngdread — sys/netgraph7/ng_device.c:432-433 — uses mbufs, no VLA */
while (m && uio->uio_resid > 0 && error == 0) {
    len = MIN(uio->uio_resid, m->m_len);
    error = uiomove(mtod(m, void *), len, uio);

/* ngdwrite — sys/netgraph7/ng_device.c:463-464 — bounds check, no VLA */
if (uio->uio_resid > IP_MAXPACKET)
    return (EIO);
```

The maintained version is also **not enabled** in `X86_64_GENERIC` (the option
`netgraph7_device` is absent), so even the fixed version is not in the default
kernel.

### (4) Even if reachable, the device is root-only

`/dev/ngdN` is created by `make_dev(&ngd_cdevsw, unit, UID=0, GID=0, MODE=0600,
"ngd%d", unit)` (`sys/netgraph/ng_device.c:286-287`). Mode `0600` means only
**root** can `open()` it. So even on a hypothetical kernel where this code were
live, the bug is a **root→kernel** issue, not an unprivileged→kernel escalation.
Per the Phase-6 hard-blocker list, a write reachable only from an already-root
context is game-over by definition — there is no privilege boundary to cross.

---

## Conclusion — which step-4 category

**(d) Genuinely not reachable on this kernel** (primary) + partial **(a) false
positive** (the "unprivileged user" impact claim is wrong on every axis):
- The vulnerable file is not in any build path.
- It cannot compile against the current kernel API.
- The maintained equivalent is already fixed.
- The character device is mode 0600 (root-only).

The VLA is a real code-quality / defense-in-depth defect in the orphaned source
text, so `fix.diff` is provided (replaces both VLAs with `kmalloc` + size
bounds). Because the code is not part of any buildable kernel, the fix cannot be
built/validated on the guest — `fix_status: not_testable`.

## Fix validation status

`not_testable` — the cited file (`sys/netgraph/ng_device.c`) is not compiled
into the default kernel and cannot be built as a module (removed cdevsw API).
There is therefore no kernel image into which `fix.diff` can be built to
exercise the path. `fix.diff` is verified to apply cleanly (`git apply --check`
rc=0) and is a correct VLA→kmalloc+bounds transformation matching the pattern
already used in the fixed `netgraph7/ng_device.c`.
