# VERDICT — DF-3210

**status: untested** (boot-path-only; not runtime-testable on the audit guest)
**impact: none** (bounded mbuf leak; no corruption, no reachable exhaustion)
**confidence: certain** (ownership contract violation provable by inspection; trigger likelihood marginal)

## Proof chain (path:line)

1. `sys/vfs/nfs/krpc_subr.c:275-276` — `mhead = m_gethdr(M_WAITOK, MT_DATA); mhead->m_next = *data;` — the
   caller's request becomes part of `mhead`'s chain **only from this point on**.
2. Error exits before :275 — `socreate` (:220-221), `sosetopt(SO_RCVTIMEO)` (:231-232), and `sobind`
   (:263-265, the reserved-port sweep loop) — all `goto out`.
3. `sys/vfs/nfs/krpc_subr.c:437-441` — `out:` frees only `mhead` (NULL on those paths) and `from`; `*data` is
   never freed and never reassigned on error ⇒ **request chain leaked**.
4. Caller-side manifestation:
   * `sys/vfs/nfs/nfs_mountrpc.c:146-147` v3 `krpc_call` error → `else` branch at :151 → `m` reassigned at
     :160 without `m_freem(m)` ⇒ v3 request leaked on early-exit errors.
   * `sys/vfs/nfs/nfs_mountrpc.c:165-166` — `return error; /* message already freed */` — false for early exits.
   * `sys/vfs/nfs/nfs_mountrpc.c:258-259` — same false comment in `md_lookup_swap`; request
     (m_get head + xdr_string_encode chain, 2 mbufs) leaked.
5. Not a UAF/double-free: for error exits **after** chaining (sosend :324-327, PROGMISMATCH :393-395 — that
   reply-chain variant is DF-3204, not re-reported), `*data` was freed exactly once inside `m_freem(mhead)`;
   callers never touch `m` after a failed call before reassignment (:146→:160) or return (:259).

## Trigger reality
`socreate(AF_INET, SOCK_DGRAM)` and `sosetopt(SO_RCVTIMEO)` do not fail in practice; `sobind` fails only when
all ~512 swept reserved UDP ports (:256-262) are taken during single-threaded early boot. Bounded: ≤1 chain
per failed call, ≤3 calls per boot.

## Why untested
Only reachable inside the boot-path mount RPCs (`#if BOOTP||NFS_ROOT`); audit guest (DragonFly 6.5-DEVELOPMENT
#0, X86_64_GENERIC, hammer2 disk root) never executes nfs_mountroot → md_mount/md_lookup_swap.

## Recommended fix
Free the unconsumed request at `out:` when it was never chained:
```diff
--- a/sys/vfs/nfs/krpc_subr.c
+++ b/sys/vfs/nfs/krpc_subr.c
@@ -437,7 +437,13 @@
  out:
-	if (mhead) m_freem(mhead);
+	if (mhead) {
+		m_freem(mhead);
+	} else if (*data) {
+		/* Early error exit before the request was chained onto mhead.
+		   Callers (nfs_mountrpc.c:166,259) expect krpc_call to consume it. */
+		m_freem(*data);
+		*data = NULL;
+	}
 	if (from) kfree(from, M_SONAME);
 	soclose(so, FNONBLOCK);
 	return error;
```
(After `gotreply`, `mhead != NULL` and `*data` is the reply — path unaffected.)

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