# DF-2570 — ng_device_rcvdata kmalloc/mtod/kfree(M_DEVBUF) heap corruption — VERDICT

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

The heap-corruption anti-pattern the finding describes is **genuinely present**
in the cited source text of `sys/netgraph/ng_device.c:334-380`, but that 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 — "heap memory corruption / double-freeable
mbuf" — is therefore a **false positive on every live axis**: the code is
unreachable, and the maintained equivalent
(`sys/netgraph7/ng_device.c`) operates on mbufs directly and has **no
analogous bug**. This is the same dead-code conclusion the sibling findings
**DF-2571** and **DF-2572** reached for the same file.

---

## What the finding claims

`sys/netgraph/ng_device.c` `ng_device_rcvdata` (the netgraph data-receive
callback) allocates a scratch buffer, immediately discards the pointer, then
frees the mbuf's data through the wrong allocator:

```c
/* ng_device_rcvdata — sys/netgraph/ng_device.c:334-380 */
339:	char *buffer;
...
363:	buffer = kmalloc(sizeof(char)*m->m_len, M_DEVBUF, M_NOWAIT | M_ZERO);  /* alloc M_DEVBUF */
364:	if(buffer == NULL) { ... return(-1); }
369:	buffer = mtod(m, char *);     /* LEAKS the kmalloc'd buffer; now == m->m_data (MBUF zone) */
371:	if( (connection->loc+m->m_len) < NGD_QUEUE_SIZE)
372:	        memcpy(connection->readq+connection->loc, buffer, m->m_len);
...
377:	kfree(buffer, M_DEVBUF);      /* FREES m->m_data through WRONG zone (M_DEVBUF) */
```

Two real defects in the source text:

1. **Memory leak.** The `kmalloc`'d buffer at line 363 is never freed — its
   pointer is overwritten at line 369 (`buffer = mtod(m, char *)`). One
   `M_DEVBUF` allocation (bucket sized to `m->m_len`) leaks per received mbuf.
2. **Wrong-zone free / heap corruption.** `kfree(buffer, M_DEVBUF)` at line 377
   operates on a pointer that is `m->m_data`, an interior pointer into the
   **mbuf zone**, not an `M_DEVBUF` allocation. Freeing it through `M_DEVBUF`
   corrupts the `M_DEVBUF` slab freelist, and when the mbuf is subsequently
   released through its own zone the same memory is freed **twice** →
   double-freeable mbuf. A single received byte suffices to trigger it.

The bug is **genuinely present 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 `netgraph7/ng_device.c optional netgraph7_device` — a *different* file. |
| Kernel config `sys/config/X86_64_GENERIC` | **No `ng_device` / `netgraph7_device` option** (grep returns nothing). |
| Compiled into running kernel | **No.** `nm /boot/kernel/kernel.debug \| grep -c ng_device` = **0** (re-confirmed this run). |
| Loadable module on disk | **No.** `/boot/kernel/` has no `ng_device.ko` / `netgraph7_device.ko`. |
| `/dev/ngd*` device nodes | **None** (`ls /dev/ngd*` → 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. Freshly reproduced for this finding (full output in
`module_build_failure.txt`, 52 error lines, `MAKE_RC` non-zero):

```
ng_device.c:111:8: error: unknown type name 'd_close_t'
ng_device.c:112:8: error: unknown type name 'd_open_t'
ng_device.c:113:8: error: unknown type name 'd_read_t'
ng_device.c:114:8: error: unknown type name 'd_write_t'
ng_device.c:119:15: error: variable 'ngd_cdevsw' has initializer but incomplete type
ng_device.c:132:25: error: 'nommap' undeclared here
ng_device.c:152:4: error: implicit declaration of function 'cdevsw_add'
ng_device.c:286:27: error: implicit declaration of function 'make_dev'
ng_device.c:419:1: error: 'ngdopen' redeclared as different kind of symbol
...
cc1: all warnings being treated as errors
Stop.
```

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 does NOT have the bug

`sys/netgraph7/ng_device.c` — the file actually referenced in `conf/files`
(as `optional netgraph7_device`) — has a **completely different architecture**
that eliminates the bug class. Its `ng_device_rcvdata` (lines 259-287) operates
on the mbuf directly and **never** touches `M_DEVBUF`:

```c
/* ng_device_rcvdata — sys/netgraph7/ng_device.c:259-287 */
priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
struct mbuf *m;
NGI_GET_M(item, m);
NG_FREE_ITEM(item);

IF_LOCK(&priv->readq);
if (_IF_QFULL(&priv->readq)) {
        _IF_DROP(&priv->readq);
        IF_UNLOCK(&priv->readq);
        NG_FREE_M(m);
        return (ENOBUFS);
}
_IF_ENQUEUE(&priv->readq, m);          /* mbuf enqueued whole; no kmalloc/copy */
IF_UNLOCK(&priv->readq);
...
return (0);
```

There is **no** `kmalloc(...M_DEVBUF)`, **no** `buffer = mtod(...)`, **no**
`kfree(...M_DEVBUF)` anywhere in the maintained `rcvdata` (grep returns empty).
The mbuf is handed intact to the readq and later drained/freed through the
mbuf zone by `m_freem` in `ngdread` (`netgraph7/ng_device.c:440`). 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

The netgraph graph that feeds `ng_device_rcvdata` (creating the node, hooking
it up, pushing data onto the hook) requires `root` to configure, and the
`/dev/ngdN` character device is created by `make_dev(&ngd_cdevsw, unit, 0, 0,
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. (The maintained `netgraph7/ng_device.c` likewise creates the
device mode `0600`.)

---

## Conclusion — which step-4 category

**(d) Genuinely not reachable on this kernel** (primary) + partial **(a) false
positive** (the "heap memory corruption / double-freeable mbuf" impact claim is
wrong on every live axis):
- The vulnerable file is not in any build path (`conf/files` references only
  `netgraph7/ng_device.c` at `:1699`; absent from `X86_64_GENERIC`).
- It cannot compile against the current kernel API (52 error lines, removed
  cdevsw/make_dev API).
- It is not in the running kernel (`nm` count = 0) and no module exists on disk.
- The maintained equivalent has **no** `kmalloc(M_DEVBUF)`/`mtod`/`kfree(M_DEVBUF)`
  in its `rcvdata` — the bug class is eliminated there.
- The character device is mode 0600 (root-only) in both the dead and maintained
  versions, so even a live instance would be root→kernel, not unprivileged.

Because the cited code cannot run, there is **no memory corruption to
reproduce** and therefore **no Phase-6 escalation chain** to develop and **no
Phase-8 patched-kernel build** to validate (there is no live kernel image into
which `fix.diff` can be built to exercise the path). The wrong-zone-free defect
is a real code-quality / defense-in-depth issue in the orphaned source text, so
`fix.diff` is provided: it removes the pointless `kmalloc`/`buffer`/`kfree`
dance and copies straight from `mtod(m, char *)` into `readq`, matching the
design already used in the fixed `netgraph7/ng_device.c`. The real resolution,
however, is to **delete the orphaned file** (it is fully superseded by
`netgraph7/ng_device.c`).

## 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 (the
`cdevsw`/`make_dev` API it uses was removed). 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 minimal
change: it deletes the leaked `kmalloc`, the `buffer = mtod` reassignment, and
the wrong-zone `kfree(buffer, M_DEVBUF)`, copying directly from the mbuf
instead — the same data-flow pattern already used in the fixed
`netgraph7/ng_device.c`.
