# DF-3408 — VERDICT

**Status: REPRODUCED (deterministic resource leak, +1 mbuf per faulted
write); fix validated.**

## Baseline

* Guest: DragonFly 6.5-DEVELOPMENT `#0` X86_64_GENERIC.
* PoC `tun_eault_leak.c`: `open("/dev/tun")`, then repeated 8192-byte
  `write()`s of a buffer whose second page is `mprotect(PROT_NONE)`.
  The first page copies through `uiomove()`; the fault on page 2 returns
  EFAULT with `uio_resid > 0`, which is precisely the leak condition.

Run 1 (run.log):

```
BEFORE: 8/146632 mbufs in use
writes: ok=0 faulted=30000
AFTER:  30007/146632 mbufs in use     (+29999 ≈ +1/write)
```

Control (run.control.log): 20000 writes with both pages readable —
mbuf count flat (30007 before and after). The leak is specific to the
uiomove-EFAULT error path, not to write volume.

## Root cause (line-accurate)

sys/net/tun/if_tun.c:875-882: the trailing `MGET` on :880-881 executes
in the same iteration that linked the previous mbuf; on a faulting
iteration the loop exits before the spare is ever linked by
`*mp = m` (:878), and the error path `m_freem(top)` (:884-885) frees
only the linked chain. In the error branch `m` is provably always the
unlinked spare: `uiomove` cannot fail while driving `uio_resid` to 0,
so `error != 0` implies `uio_resid > 0` implies the `MGET` ran in the
final iteration. Identical shape to tap(4) DF-3406.

## Impact

Unbounded kernel mbuf leak (memory-exhaustion DoS) repeatable by a
privileged caller — `tunopen()` is gated by
`caps_priv_check(SYSCAP_RESTRICTEDROOT)` (if_tun.c:283). With
`kern.ipc.nmbclusters=33296` on this guest, ~33k faulted writes exhaust
the cluster pool; the leak is otherwise unbounded across the lifetime of
the open fd. No unprivileged reachability, so no escalation chain —
root-gated by construction.

## Fix validation

`fix.diff` adds `m_freem(m)` (the spare) to the error branch. Combined
into the same patched kernel as DF-3407's fix, rebuilt, rebooted, and
the exact PoC re-run (run.fix.log): 30000 faulted writes leave the mbuf
count flat. Baseline leak vs patched flat = fix confirmed.

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