# DF-0410 — PoC reproduction

## What this is

A **userspace harness** that proves the heap OOB read+write primitive in
`ng_encode_string()` (`sys/netgraph7/netgraph/ng_parse.c`) is real, plus a
real-kernel module build that validates the fix compiles and works at the
disassembly level.

**There is no live kernel PoC** because the vulnerable function lives in
**netgraph7**, DragonFly's opt-in parallel netgraph stack, which is NOT compiled
into the default `X86_64_GENERIC` kernel (`sys/Makefile.modules` builds v1
`sys/netgraph/` unless `WANT_NETGRAPH7` is defined; verified absent on the
guest: `nm /boot/kernel/kernel | grep -c ng_encode_string` = 0). The bug is
also **root-gated**: the only trigger path (an `NGM_BINARY2ASCII` control
message on a sizedstring field) requires the netgraph7 control socket, whose
`ngc_attach` enforces `SYSCAP_RESTRICTEDROOT` (`ng_socket.c:182`). See
`VERDICT.md` for the full analysis.

## How to reproduce (harness)

```sh
./build.sh     # builds df0410_vuln and df0410_fixed (-DAPPLY_FIX)
./run.sh       # runs both; prints OOB read count + heap overflow size
```

### Expected (vulnerable build)

```
ng_encode_string returned 25 bytes of encoded output:
  "\x001234567890123456789"
OOB READ: 23 bytes were encoded from BEYOND raw's NUL terminator (strlen(raw)=0 but 23 data bytes appear in output).
OOB WRITE: loop wrote 26 bytes into a 3-byte allocation => 23-byte HEAP OVERFLOW.
[no fix] Heap overflow CONFIRMED: alloc=3 < written=26.
```

### Expected (fixed build)

```
[APPLY_FIX] allocation now slen*4+3=83 >= 26 written => overflow GONE.
```

## The bug

`ng_sizedstring_unparse` (`ng_parse.c:924`) reads an attacker-controlled
`u_int16_t slen` and calls `ng_encode_string(raw, slen)` (`:925`).
`ng_encode_string` (`:1832`) allocates `strlen(raw)*4+3` but loops `slen` times
(`:1837`). If `slen > strlen(raw)`:

- the loop reads past `raw`'s NUL terminator → kernel heap **info leak** (encoded
  bytes returned to userspace via the `NGM_BINARY2ASCII` reply);
- the loop writes up to `slen*4+3` bytes into the undersized buffer → **heap
  overflow** (worst case: `raw[0]='\0'`, `slen=65535` ⇒ ~262 KB overflow from a
  3-byte allocation).

## The fix

`fix.diff` — bound the allocation on `slen`, not `strlen(raw)`:

```diff
-	cbuf = kmalloc(strlen(raw) * 4 + 3, M_NETGRAPH_PARSE,
+	cbuf = kmalloc(slen * 4 + 3, M_NETGRAPH_PARSE,
 		       M_WAITOK | M_NULLOK);
```

Validated: applies with `git apply -p1`; the real netgraph7 module builds under
`-Werror` both ways; disassembly of the fixed object drops the `strlen` call and
keys the allocation on `slen`. See `module_build.log`.
