ng_fec_choose_port dereferences ether/IP headers without mbuf length validation: OOB read
Summary
ng_fec_choose_port: eh=mtod(m)(:897) ip=mtod+sizeof(ether_header)(:899-900) ip6=same(:902-903). No m_len/m_pkthdr.len check no m_pullup. INET reads ntohl(ip->ip_dst)(:930) INET6 reads ip6_dst.s6_addr[15](:935). Short/fragmented mbuf -> OOB read past m_data. Same as DF-0504(ng7). Fix: m_pullup before deref.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0526 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| trigger.sh | trigger-source | harness; documents DF-0529 blocker | 1.5 KB | view raw |
| build.sh | build-script | 228 B | view raw | |
| run.sh | run-script | 216 B | view raw | |
| fix.diff | suggested-fix | m_pullup in ng_fec_start caller | 651 B | view raw |
| VERDICT.md | verdict | source confirmation + DF-0529 blocker | 3.2 KB | β raw |
| build.log | build-log | fix compile-validated RC=0 | 63 B | view raw |
| env.txt | environment | 829 B | view raw | |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
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:
// :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 reacheschoose_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:
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.koproduced with the fix in source); fix_status = not_testable: the panic path (DF-0529) prevents creating a node to exercisechoose_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.
Fix verification
not_testablecompile validated
see evidence pack
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. ng_fec_choose_port no m_pullup before ether/IP deref -> OOB read. Blocked by DF-0529 constructor panic.
No comments yet.