# DF-3541 — VERDICT

**Status: reproduced. Impact: dos (unbounded kernel heap/mbuf exhaustion). Confidence: certain.**

## Reproduction

Guest: DragonFly 6.5-DEVELOPMENT #0 (stock INVARIANTS kernel). v1 stack:
stock /boot/kernel/netgraph.ko + ng_socket.ko, ng_eiface.ko rebuilt from
/usr/src with DF-3486's validated fix (without it, node creation panics
first — DF-3486, already filed; this finding sits behind it).

`eiface_leak.c` (v1 ABI: NG_VERSION 2, u_int16_t arglen, header order
flags/token/typecookie/cmd; sockaddr data at offset 2; node addressed as
"name:"):

1. control socket (node named "eileak" via bind), data socket connected;
2. NGM_MKPEER eiface on hook "ether";
3. sleep 2 (DF-3486 deferred ether_ifattach) → ngethN exists, **DOWN**
   (flags=\<BROADCAST,SIMPLEX,MULTICAST,PROXY\>, no IFF_UP);
4. 500 × 2048-byte packets sent to the "ether" hook.

Result (run.log / leak_before.txt / leak_after.txt):

- every sendto returns ENETDOWN (errno 50) — the ng_eiface.c:514-515
  path executes;
- mbuf clusters in use: 256 → **756** (+500, exactly one per packet);
  "mbufs and mbuf clusters allocated to data": 268 → 768 (+500).

Control test (same unfixed module): interface brought UP during the
trigger's wait window → clusters 756 → 756, zero growth (packets
consumed by ether_input). This isolates the ENETDOWN path as the leak.

Unprivileged check: uid 1001 → EPERM at the control socket
(unpriv_check.txt) — root-gated configuration, as with all v1 netgraph
control paths.

## Root cause (source trace)

- sys/netgraph/eiface/ng_eiface.c:514-515 — `return (ENETDOWN)` with no
  `m_freem(m)`;
- ownership passed by the framework: v1 ng_send_data
  (sys/netgraph/netgraph/ng_base.c:1684-1687) hands `m` to rcvdata and
  returns; NG_SEND_DATA callers NULL their mbuf pointer
  (sys/netgraph/netgraph.h:224-229), so nobody else can free it;
- sibling v1 nodes free on this path (ng_frame_relay.c:415-417) and the
  ng7 twin frees explicitly (NG_FREE_M before the same ENETDOWN return,
  sys/netgraph7/eiface/ng_eiface.c rcvdata) — deviation, not convention.

Impact: each packet to a DOWN ngethX through its ether hook permanently
leaks one mbuf (+cluster). At line rate this exhausts the mbuf map —
kernel memory exhaustion DoS (Medium: requires a root-built netgraph
path feeding the interface; the traffic itself can be unprivileged).

## Exploit chain

DoS only — no memory-safety violation (no overwrite; pure leak).

## Fix validation (fix.diff)

`m_freem(m)` added before the ENETDOWN return (one-line, mirrors the
ng7 twin). Rebuilt module, identical test (interface DOWN, 500×2048):
clusters 256 → 256, **zero growth**; ENETDOWN still returned per send
(fix_run.log). fix_status: fixed.

## Notes

- The DF-3486 unblock patch is scaffolding to make the module testable
  on stock kernels; it is NOT part of this finding's fix (fix.diff
  contains only the m_freem line).
- Meta leaked? No — meta was already freed earlier (NG_FREE_META at
  ng_eiface.c:507); only the mbuf leaks.

## 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.
