# DF-0610 — Verdict

## Verdict: NOT REPRODUCED (latent — module unbuildable on this kernel)

**Impact:** none (the buggy code path is unreachable at runtime on the
current DragonFlyBSD master tree)

**Confidence:** certain — confirmed by line-by-line source trace AND by
runtime attempt (`kldload ng_nat` → "No such file or directory"; the
netgraph7_nat module has no compiled artifact and cannot be built from
the in-tree source).

---

## The bug is REAL at the source level

`sys/netgraph7/ng_nat.c:643-654` — the `NGM_NAT_PROXY_RULE` handler:

```c
643:	case NGM_NAT_PROXY_RULE:
644:	    {
645:		char *cmd = (char *)msg->data;
646:
647:		if (msg->header.arglen < 6) {
648:			error = EINVAL;
649:			break;
650:		}
651:
652:		if (LibAliasProxyRule(priv->lib, cmd) != 0)
653:			error = ENOMEM;
654:	    }
655:		break;
```

`cmd` points at `msg->data`, which holds exactly `msg->header.arglen`
attacker-supplied bytes with **no guaranteed NUL terminator**.
`LibAliasProxyRule()` treats its argument as a C string and walks it
looking for a terminating `\0`, so with non-NUL wire data it reads
`cmd[arglen]` and beyond — a heap OOB read past the allocation.

The sibling handlers `NGM_NAT_REDIRECT_PORT` / `REDIRECT_ADDR` /
`REDIRECT_PROTO` do it correctly (e.g. `ng_nat.c:423-427`):

```c
bcopy(rp->description, entry->rdr.description, NG_NAT_DESC_LENGTH);
/* Safety precaution. */
entry->rdr.description[NG_NAT_DESC_LENGTH - 1] = '\0';
```

— bounded copy + forced NUL. The proxy-rule path omits both steps. The
finding's root-cause analysis is accurate.

## Why it does NOT reproduce on this kernel

The `netgraph7_nat` module **cannot be compiled or loaded** on the current
DragonFlyBSD master tree. Two of its required source dependencies are
absent from `sys/` itself:

1. **`sys/netinet/libalias/` — directory does not exist.**
   `sys/conf/files:1735-1739` declares the module's libalias sources:
   ```
   1735: netinet/libalias/alias.c      optional netgraph7_nat
   1736: netinet/libalias/alias_db.c   optional netgraph7_nat
   1737: netinet/libalias/alias_mod.c  optional netgraph7_nat
   1738: netinet/libalias/alias_proxy.c optional netgraph7_nat
   1739: netalias/libalias/alias_util.c optional netgraph7_nat
   ```
   None of these files exist on disk (verified on both host `sys/` and
   guest `/usr/src/sys/`). The module calls `LibAliasInit`,
   `LibAliasProxyRule`, `LibAliasIn`, etc. — all unresolved.

2. **`m_megapullup()` — referenced at `ng_nat.c:692`, defined nowhere in `sys/`.**
   `grep -rn m_megapullup sys/` returns only the single reference at
   `ng_nat.c:692`; there is no definition, so the link fails.

**Runtime confirmation (guest `6.5-DEVELOPMENT #0`):**

```
# kldload -n ng_nat
kldload: can't load ng_nat: No such file or directory
rc=1
# find /boot /modules /usr/obj -name 'ng_nat*'   (no output — no .ko anywhere)
```

The PoC (`poc.c`) compiles cleanly and, after `kldload ng_socket`,
`socket(AF_NETGRAPH, NG_CONTROL)` succeeds — but `sendto` to the "nat"
node fails with `ENOENT` because no ng_nat node can ever exist on this
kernel:

```
sendto: No such file or directory (errno=2)
If errno=ENOENT/ENODEV the 'nat' node does not exist
(ng_nat module not loaded / not buildable on this tree).
RUN_EXIT=2
```

This is case (d) from the procedure: *genuinely not reachable on this
kernel* — the sink is in a module whose source dependencies are missing
from the master tree itself (not a config option an operator could flip).

This matches the DF-0611 precedent (same netgraph7_nat module, same
latent classification, same `not_reproduced` / `none` verdict).

## No escalation chain

The primitive is a heap OOB **read**, and it is latent (unreachable).
There is no write primitive, no corruption, no chain to develop. Even if
the module were buildable, the trigger requires root
(`SYSCAP_RESTRICTEDROOT` on `ngc_attach` at
`sys/netgraph7/socket/ng_socket.c:182-184`), so there is no privilege
boundary to cross — it would be a root→kernel local DoS at best.

## Fix

`fix.diff` applies cleanly (`patch -p1 --dry-run` → rc=0, hunk #1
succeeded at line 642) and closes the OOB read by giving
`LibAliasProxyRule()` a handler-allocated, NUL-terminated copy of the
wire data — matching the bounded-copy + force-NUL pattern already used
by the sibling `NGM_NAT_REDIRECT_*` handlers:

```c
cmd = kmalloc(msg->header.arglen + 1, M_NETGRAPH,
    M_WAITOK | M_NULLOK | M_ZERO);
if (cmd == NULL) { error = ENOMEM; break; }
bcopy(msg->data, cmd, msg->header.arglen);
cmd[msg->header.arglen] = '\0';
if (LibAliasProxyRule(priv->lib, cmd) != 0)
    error = ENOMEM;
kfree(cmd, M_NETGRAPH);
```

The fix uses only APIs already present and compiling in this exact file
(`kmalloc(...,M_NETGRAPH,M_WAITOK|M_NULLOK|M_ZERO)` at lines 275/397/456/508;
`bcopy` at 423/475/529; `kfree(...,M_NETGRAPH)` at 283/411/467/520).

## Fix validation: not_testable

The bug did not reproduce (module unbuildable), so there is no
runtime "before" marker to compare against a patched kernel. The
`netgraph7_nat` module cannot be built even with the fix applied
(the missing libalias sources and `m_megapullup` still block the
link), so a single-fix kernel build + reboot test is impossible.

What WAS validated:
- `git apply --check` passes on the host `sys/` tree.
- `patch -p1 --dry-run` and `patch -p1 --forward` both succeed on the
  guest `/usr/src` (hunk #1 at line 642).
- The patched source reads correctly (bounded copy + force-NUL before
  the `LibAliasProxyRule` call, `kfree` after).
- The fix uses only in-file APIs (no new dependencies).

`fix_status = not_testable` — diff applies + is syntactically consistent
with the file's existing patterns; runtime test impossible because the
module is unbuildable on this tree (latent).

## PoC changes

- Wrote `poc.c` (the finding's README referenced it but it was never
  seeded). Self-contained C that opens an `AF_NETGRAPH` control socket,
  builds a `struct ng_mesg` with `typecookie=NGM_NAT_COOKIE`,
  `cmd=NGM_NAT_PROXY_RULE`, `arglen=64` of non-NUL `'A'` bytes, and
  `sendto`s it to a node named "nat". Compiles and runs on the guest;
  demonstrates unreachability (`sendto` → ENOENT, no ng_nat node).
- Wrote `build.sh` / `run.sh` repro scripts.
- Authored standalone `fix.diff` (git-apply-able, supersedes the finding
  markdown's proposal with the same logic — clean hunk against current
  line numbers).

## Recommendation

Apply `fix.diff` to `sys/netgraph7/ng_nat.c` so the missing-validation
pattern is closed whenever the module is reintroduced. Separately decide
whether to resurrect `sys/netinet/libalias/` + `m_megapullup()` or to
retire `netgraph7_nat` from `sys/conf/files`. Severity Low stands as a
latent code-level defect; real-world exposure today is NONE.
