# DF-3278 — VERDICT

**NOT REPRODUCED within the guest budget — but the defect is
code-certain and the vulnerable path was driven live at scale.
Classified not_reproduced (window not hit), not false_positive.**

## The defect (code analysis, certain)

`bridge_fragment()` (sys/net/bridge/if_bridge.c:4367-4416):

```c
	error = ip_fragment(ip, &m, ifp->if_mtu, ifp->if_hwassist,
		    CSUM_DELAY_IP);
	if (error)
		goto out;
	for (m0 = m; m0; m0 = m0->m_nextpkt) {          /* (C) UAF read */
		if (error == 0) {
			if (snap) {
				M_PREPEND(m0, sizeof(struct llc), M_NOWAIT);
				if (m0 == NULL) { error = ENOBUFS; continue; }  /* (A) */
				...
			}
			M_PREPEND(m0, ETHER_HDR_LEN, M_NOWAIT);
			if (m0 == NULL) { error = ENOBUFS; continue; }      /* (A) */
			bcopy(eh, mtod(m0, caddr_t), ETHER_HDR_LEN);
		} else
			m_freem(m);                               /* (B) double free */
	}
```

- (A) `M_PREPEND` on `M_NOWAIT` allocation failure **frees the mbuf
  chain** and leaves the variable NULL (sys/sys/mbuf.h:463-479;
  `m_prepend()` does `m_freem(m); return NULL`,
  sys/kern/uipc_mbuf.c:1508-1511).
- After the first failure, `error = ENOBUFS`, so every subsequent
  loop iteration takes the `else` branch and calls `m_freem(m)` where
  `m` is fragment #1 — already freed in the failing iteration if the
  failure was on fragment #1, or freed on the first `else` iteration
  and then freed AGAIN on every following one (fragment chains of N ≥ 3
  give N−2 double frees).
- (C) the loop increment `m0 = m0->m_nextpkt` reads the `m_nextpkt`
  of the mbuf just freed by M_PREPEND — a use-after-free read.

## Reachability (verified live on the guest)

`bridge_fragment()` is called only from `bridge_pfil()`
(if_bridge.c:4115-4121) on the member-OUT path when `pfil_member` is
set, inet pfil hooks are registered, and `m_pkthdr.len > member
if_mtu`.  All preconditions were established and verified:

- `kldload ipfw` + `ipfw add 100 allow all from any to any` (ipfw2
  initialized; `inet_pfil_hook.ph_hashooks > 0`).
- bridge0 with both members at MTU 600; injector writes valid-IP
  1500-byte UDP frames into tap0.
- Verified: 50/50 test frames fragmented (`netstat -s -p ip`:
  "50 output datagrams fragmented"); a 6×500,000-frame flood pushed
  **500,050 datagrams through bridge_fragment** with zero bad sums.
- To force the M_NOWAIT failure, kernel mbuf clusters were pinned via
  8 UDP loopback sockets with 2 MB SO_RCVBUF never read
  (kern.ipc.maxsockbuf raised to 8 MB): serial console showed
  `Warning: objcache(mbuf cluster) exhausted on cpu1..cpu5`.
- Under this pressure the fragment re-header loop was exercised, but
  the exact instant where `m_prepend()` must allocate (fragment with
  zero leading space) AND the allocation fails was never observed:
  both heavy attempts ended with the box wedged by cluster exhaustion
  (mbuf starvation DoS from the drainer itself) rather than the
  targeted double free.  Deterministically forcing a per-cpu objcache
  failure at one specific allocation site is not possible from
  userland without kernel knobs.

## Impact if triggered

Kernel heap double-free / UAF read on M_DEVBUF mbufs, driven by
forwarded traffic, preconditions = root-configured bridging with pfil
(ipfw/pf) and an MTU-mismatched member plus transient mbuf pressure.
Given the guest's lack of SMAP/SMEP/KASLR this is plausibly
exploitable, but no exploit was developed since the primitive itself
could not be fired.

## Files

- frag_flood.c — final injector (valid checksum — computed byte-wise;
  an earlier version's LE-word sum and a strict-aliasing reorder each
  produced invalid checksums that bridge_ip_checkbasic dropped, which
  is why early runs showed zero fragmentation).
- run.sh — setup + flood.
- run.log — counters proving the fragment path ran at scale + the
  exhaustion warnings.

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