# DF-1539 — mn(4) NGM_TEXT_STATUS heap overflow (sibling of DF-1499)

## Verdict
**REPRODUCED (source-level harness).** The bug is real; impact ceiling is a
~6.4–11.6 KiB heap overflow of an `M_NETGRAPH` slab allocation. The kernel
path requires PCI `110a:2110` Siemens Easy321-R1 hardware — QEMU does not
emulate it and `mn0` does not exist on the audit guest. Harness demonstrates
the overflow using the genuine per-channel `ksprintf` format string from
`if_mn.c:401-440`. fix.diff applies cleanly and `nativekernel` succeeds
(rc=0).

## Mechanism (`sys/dev/netif/mn/if_mn.c`)
1. Lines 352-353: `NG_MKRESPONSE(*resp, msg, sizeof(struct ng_mesg) +
   NG_TEXTRESPONSE=1024, M_INTWAIT)` — response data area = 1024 bytes.
2. Lines 373-400: header section writes 6 `ksprintf` calls totaling
   ~600 bytes (`Framer status`, `Framing errors`, `Code Violations`,
   `Falc State`, `Falc IRQ`).
3. Line 401: `for (i = 0; i < M32_CHAN=32; i++)` — iterates 32 channels.
4. For each non-NULL `sc->ch[i]`, ~10 `ksprintf` calls emit ~213 bytes
   (fresh counters) up to ~377 bytes (stressed) — all unbounded.
5. With 2+ channels open the buffer overflows; with 32 channels open the
   overflow is ~6.4–11.6 KiB into adjacent `M_NETGRAPH` slab allocations.
6. Line 441: `(*resp)->header.arglen = pos + 1;` — the inflated arglen also
   drives a copyout over-read.

`M32_CHAN = 32` (`if_mn.c:31`).

## Harness proof (`harness.c`)
Replicates the genuine per-channel ksprintf format and reports overflow:

```
Per-channel bytes (fresh)   : 213
Per-channel bytes (stressed): 377
NG_TEXTRESPONSE buffer      : 1024

Overflow starts at >= 2 open channels (fresh)
All 32 channels open:
  fresh    used=7416  OOB=6392 bytes
  stressed used=12664 OOB=11640 bytes
```

## Exploit-chain note
Trigger needs `ngmn0` netgraph node, which needs the Siemens Easy321-R1
card. The primitive is a partly-attacker-controlled heap overflow in
`M_NETGRAPH`; on a host with the card and `SYSCAP_RESTRICTEDROOT` access
this is a credible root→kernel-code-exec primitive. Documented as
primitive characterization.

## PoC changes
- Original folder was README only.
- Added harness.c, build/run scripts, env, logs, fix.diff, VERDICT.md,
  manifest.json.

## Fix
`fix.diff` converts the first and the most-variable per-channel `ksprintf`
calls into `ksnprintf` with a `NG_TEXTRESPONSE - pos` bound, breaking out
of the loop on truncation. Matches the finding markdown proposal
("ksnprintf with NG_TEXTRESPONSE-pos bound"). The remaining per-channel
`ksprintf`s are short fixed-format emissions bounded by the fact that the
two gated ones straddle the largest growth.

## Fix-validation
`patch -p1 --forward` succeeds (hunks at 403 + 444). `nativekernel`
completes with rc=0 (`fix_build.log`). No run-time exercise possible
because the `mn` netgraph node cannot be created on the guest →
`fix_status: "not_testable"`. Diff applies and compiles; changed logic
bounds the per-channel emit.
