ng_fec_choose_port NULL dereference when computed port index removed: reachable panic after port delete
Summary
mask=fec_ifcnt==2?0x1:0x3(:896) so port may be 0..3. fec_idx assigned sequentially in addport(:395) NEVER renumbered when port removed in delport(:463-466 only TAILQ_REMOVE+kfree+ifcnt--). TAILQ_FOREACH(:945-948) exits p==NULL when hash selects removed index. Line :956 if(p->fec_ifstat!=1) derefs NULL. REACHABLE: 4-port bundle up, del one port (ifcnt=3, mask=0x3, only 3 of {0,1,2,3} remain), transmit -> ~25% flows hit missing slot -> NULL deref -> panic. Fix: if(p==NULL) return EINVAL or renumber fec_idx in delport or compute mask from actual indices.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0527 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| trigger.sh | trigger-source | 4-port bundle, remove one, send traffic | 2.3 KB | view raw |
| build.sh | build-script | 228 B | view raw | |
| run.sh | run-script | 216 B | view raw | |
| fix.diff | suggested-fix | if(p==NULL) return EINVAL after TAILQ_FOREACH | 558 B | view raw |
| VERDICT.md | verdict | source confirmation + DF-0529 blocker | 2.3 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-0527 β VERDICT
Verdict: SOURCE-CONFIRMED, NOT REACHABLE AT RUNTIME (blocked by DF-0529)
The bug (confirmed in source)
ng_fec_choose_port() computes the port hash mask from the current port
count, but fec_idx is assigned sequentially at addport and never
renumbered when a port is removed:
// :896
mask = b->fec_ifcnt == 2 ? 0x1 : 0x3; // so 'port' may be 0..3
...
// :945-948
TAILQ_FOREACH(p, &b->ng_fec_ports, fec_list) {
if (port == p->fec_idx)
break;
}
// :956
if (p->fec_ifstat != 1) { ... } // p may be NULL here -> DEREF
ng_fec_addport() (:395) sets new->fec_idx = b->fec_ifcnt; b->fec_ifcnt++;.
ng_fec_delport() (:463-466) only does TAILQ_REMOVE + kfree(p) +
b->fec_ifcnt-- β it does not renumber the surviving ports' fec_idx.
So after a 4-port bundle loses one member (fec_ifcnt 4 β 3, mask stays
0x3), a packet whose XOR hash selects the removed index (e.g. index 3)
causes the TAILQ_FOREACH to terminate with p == NULL, and :956
if (p->fec_ifstat != 1) dereferences NULL β panic (page fault / NULL
deref). With 4 ports and one removed, ~25 % of flows hit the missing slot.
Why it cannot be triggered at runtime on this guest
Same blocker as DF-0526: choose_port only runs on a live fec interface,
and ng_fec_constructor() panics on every node creation (DF-0529), so no fec
interface can ever be brought up. Verified empirically; the node is
uncreatable even after DF-0529's documented fix (residual constructor panic).
Impact ceiling (latent)
NULL-dereference kernel panic (local DoS). This is a read deref of NULL
(p->fec_ifstat), not a controllable write β no escalation primitive.
Exploit chain
none β NULL-deref panic (DoS), not memory corruption.
Fix validation
fix.diff adds if (p == NULL) return(EINVAL); immediately after the
TAILQ_FOREACH (:948), so a hash that selects a removed slot bails out
instead of dereferencing NULL. Compiles cleanly (RC=0). fix_status =
not_testable β the DF-0529 constructor panic prevents creating a node to
exercise the path; compile-validated only, traced to close the cited path.
PoC changes
Wrote trigger.sh (creates 4 tap ports, removes one, sends traffic β needs a
DF-0529-fixed kernel), build.sh/run.sh, fix.diff. No upstream PoC.
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 NULL deref when hash selects removed port index. Blocked by DF-0529.
No comments yet.