# DF-3527 — VERDICT

**Status: REPRODUCED (leak, certain). Fix VALIDATED (fixed).**

Finding: `ng_iface_rcvmsg()` returns `EBUSY` early (sys/netgraph/iface/ng_iface.c:666)
when the interface is `IFF_UP`, skipping the `kfree(msg, M_NETGRAPH)` at the
function tail (:732). Under the v1 netgraph contract the receiving node owns the
message ("It is up to the message handler to free the message",
sys/netgraph/netgraph/ng_base.c:1176-1200); the sender allocates
`kmalloc(len + 1, M_NETGRAPH)` per `sendto()`
(sys/netgraph/socket/ng_socket.c:246-258, comment at :256: "The callee will free
the xmsg when done"). Each `NGM_IFACE_POINT2POINT`/`NGM_IFACE_BROADCAST` message
sent while the ng iface is UP leaks `sizeof(struct ng_mesg) + arglen + 1` bytes.

## How it was reproduced

The stock guest cannot create any v1 ng_iface node at all (DF-3485 kfree(NULL)
panic in `ng_iface_get_unit`, then DF-3486 if_attach-from-netisr panic — the
latter re-verified live for this pass at ng_iface.c:605, see
`panic.iface_residual_df3486.txt`: `if_attach() <- ng_iface_constructor+0x22d <-
ng_mkpeer() <- ng_generic_msg()`, "panic: try holding ifnet lock in netisr").
The PoC therefore runs with ONLY the two already-validated fixes for those
known findings applied to the guest's /usr/src (module-only build):
DF-3485/fix.diff + DF-3486/fix.diff. Neither touches `ng_iface_rcvmsg`.

Steps (all as root; PF_NETGRAPH control sockets require
`caps_priv_check(SYSCAP_RESTRICTEDROOT)`, ng_socket.c:172-176):

1. `kldload ng_socket; kldload ./ng_iface.ko`
2. `ngctl mkpeer . iface inet inet` (creates node ng0); `ifconfig ng0 up`
   → flags `<UP,POINT2POINT,RUNNING,...>` — EBUSY path armed.
3. Control: 512 × `NGM_IFACE_GET_IFNAME` (normal error-free path) →
   vmstat -m netgraph **unchanged** (2.01K/40.0M).
4. Leak: 2048 × `NGM_IFACE_POINT2POINT`, arglen=16384, on UP ng0 →
   all 2048 sendto()s return **EBUSY**; vmstat -m netgraph
   **2.01K/40.0M → 4.01K/80.0M** (+2048 allocations ≈ +40MB for a
   33.6MB payload — allocator granularity).
5. Repeat runs accumulate linearly and never return: 40M → 80M → 120M
   (run.log, run.2.log). kmem limit for the allocator is 390M ⇒ ~10 runs
   of this size exhaust it: unbounded kernel-heap leak / DoS.

## Fix validation

`fix.diff` (one hunk + label): `error = EBUSY; goto done;` instead of the
early return, so the epilogue frees the message (and resp handling is
unchanged; `resp` is NULL on that path).

- baseline module: 2048×EBUSY → +2048 allocs / +40MB, persistent.
- fixed module (same kernel, module swap only): 2048×EBUSY → **+0 allocs /
  +0 bytes** (6.01K/120M flat across control AND leak runs; the standing
  120MB is the earlier baseline leak, unrecoverable as expected).
- semantics preserved: messages still rejected with EBUSY while UP.
  See fix_build.log / fix_run.log.

## PoC changes vs the seed sketch

- Node address must be the ABSOLUTE form `"ng0:"` — a bare `"ng0"` is parsed
  by `ng_path_parse()` (ng_base.c) as a relative hook path on the sender's
  own socket node → ENOENT (first attempt failed with this).
- v1 `struct ng_mesg` built locally: header.arglen is **u_int16_t** in v1
  (not u_int32_t as in ng7), NG_VERSION=2, NG_CONTROL=2, SOCK_DGRAM.
- Drain the synchronous response after each successful send (else the
  socket receive buffer fills → ENOBUFS on response-carrying commands).
- vmstat -m parsing: DFly columns are `type InUse MemUse ...` with K/M
  suffixes.

## Environmental notes

- Guest: DragonFly 6.5-DEVELOPMENT #0 (X86_64_GENERIC, INVARIANTS), cc 8.3.
- The ng_iface module used for the leak run carries DF-3485+DF-3486 fixes
  only; those are prerequisites for the node to exist at all and are
  unrelated to rcvmsg.
- Side-check recorded in mppc_loadcheck.log: stock `ng_mppc.ko` does NOT
  kldload on this kernel ("interface rc4.1 already present in the KLD
  'kernel'" — the module Makefile statically links crypto/rc4/rc4.c while
  the kernel also compiles it, sys/conf/files:2268). Packaging tangle, not
  a memory-safety issue; documented for the DF mppc clean verdict.

## Bottom line

Real, unbounded, persistent M_NETGRAPH kernel-heap leak; root-gated
(netgraph control socket), hence Low severity — but trivially repeatable to
kmem exhaustion. One-line-class fix validated live: leak gone, behavior
identical.

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
