# DF-0526 — VERDICT

## Verdict: SOURCE-CONFIRMED, NOT REACHABLE AT RUNTIME (blocked by DF-0529)

## The bug (confirmed in source)

`ng_fec_choose_port()` (`sys/netgraph/fec/ng_fec.c:876`) dereferences the
ethernet/IP/IP6 headers of the head mbuf **without any `m_pullup()` and without
any length check**:

```c
// :897
eh  = mtod(m, struct ether_header *);
// :899-900
ip  = (struct ip *)(mtod(m, char *) + sizeof(struct ether_header));
// :902-903
ip6 = (struct ip6_hdr *)(mtod(m, char *) + sizeof(struct ether_header));
...
// :930  (case M_FEC_INET)
port = (ntohl(ip->ip_dst.s_addr) ^ ntohl(ip->ip_src.s_addr)) & mask;   // DEREF
// :935  (case M_FEC_INET6)
port = (ip6->ip6_dst.s6_addr[15] ^ ip6->ip6_src.s6_addr[15]) & mask;   // DEREF
```

The pointers are computed unconditionally from `mtod(m)`; the actual
dereferences happen in the `switch` on `m_flags`.  A short or fragmented mbuf
whose head mbuf does not contain a full `ether_header + ip` (or `ip6`) header
causes an **out-of-bounds read past `m_data`** — an info leak of adjacent slab
/ stack bytes.  This is the same defect as the netgraph7 twin DF-0504.

This is an **OOB READ** (info leak), not a write.

## Why it cannot be triggered at runtime on this guest

`ng_fec_choose_port()` is called only from `ng_fec_start()` (`:1013`), which
runs when packets are dequeued from a **live fec interface's** send queue.
A live fec interface requires a successfully-constructed ng_fec node.  But:

> **`ng_fec_constructor()` panics on EVERY node creation (DF-0529).**  See
> DF-0529/VERDICT.md.  No ng_fec node can ever be brought up, so no packet
> ever reaches `choose_port`.

This was confirmed empirically: every `ngctl mkpeer .: fec ...` panics the
guest in the constructor before any interface exists.  Even after applying
DF-0529's documented fix, the constructor still panics (residual kfree
cascade), so the node remains uncreatable.  Therefore DF-0526 is genuinely
unreachable on this kernel — not because the code is dead, but because a
*separate, earlier* bug (DF-0529) panics first.

## Impact ceiling (latent, if the node were creatable)

Local information leak of kernel memory adjacent to the head mbuf's `m_data`.
On a system where an admin had configured a working fec bundle and user
traffic was routed through it, a short/malformed packet could leak slab bytes.
No write primitive is derivable from this read alone.

## Exploit chain

`none` — this is an OOB read, not memory corruption.  No escalation chain.

## Fix validation

`fix.diff` adds an `m_pullup()` in the caller `ng_fec_start()` (the correct
location, since `choose_port` takes the mbuf by value and cannot propagate a
reallocated pointer), dropping short mbufs cleanly:

```c
m0 = m_pullup(m0, sizeof(struct ether_header) + sizeof(struct ip));
if (m0 == NULL) { IFNET_STAT_INC(ifp,ierrors,1); priv->if_error=ENOBUFS; return; }
```

- compiles cleanly (RC=0, `ng_fec.ko` produced with the fix in source);
- `fix_status = not_testable`: the panic path (DF-0529) prevents creating a
  node to exercise `choose_port`, so the fix cannot be runtime-validated on
  this guest.  Compile-validated only; traced to close the cited code path.

## PoC changes

Wrote `trigger.sh` (harness entry; notes the DF-0529 blocker), `build.sh`/
`run.sh`, `fix.diff`.  No upstream PoC existed.
