β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-4267

RX ring collapse: constant-condition ternary links all 64 RX descriptors (software and hardware next pointers) to descriptor 0

Summary

wb_list_rx_init computes nexti = (WB_RX_LIST_CNT - 1) ? 0 : i + 1 - the condition is the compile-time constant 63, not i == 63, so nexti is always 0. Every descriptor's software wb_nextdesc and hardware wb_next point at descriptor 0: the intended closed 64-ring becomes a 1-deep self-ring. The chip completes desc[0], follows next back to desc[0], finds it unrecycled and suspends RX -> per-frame receive-engine restart; 63 of 64 pre-allocated RX buffers are unreachable dead state. A DragonFly-port typo (the TX twin in the same file is correct; FreeBSD 4.11 has the right form). Deterministic logic harness: DFly ring reaches 1/64 vs FreeBSD 64/64. One-token fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4267 Β· 14 files
FileTypeDescriptionSize
dfwb3_rig.c β€” 40.9 KB view raw
Makefile β€” 105 B ↓ download
kld_run.sh β€” 2.8 KB view raw
kld_build.log β€” 1.1 KB view raw
run.kld.log β€” 1.7 KB view raw
run.kld.dmesg.log β€” 4.5 KB view raw
run.kld_fixed.log β€” 4.8 KB view raw
fix_validated.diff β€” 2.2 KB view raw
env.kld.txt β€” 491 B view raw
wb_logic.c β€” 4.2 KB view raw
logic_run.log β€” 719 B view raw
run.sh β€” 111 B view raw
VERDICT.md β€” 4.0 KB ↓ raw
verdict.json β€” 3.7 KB view raw
VERDICT.md
↓ download raw

VERDICT β€” DF-4267 (RX ring collapse in wb_list_rx_init, if_wb.c:909)

Status: reproduced (deterministic, production code) β€” fix validated: fixed.

Method

Same evidence class as the accepted DF-4266 verification: the guest has no W89C840F NIC, but device wb is built into X86_64_GENERIC, so the driver's real static functions live in the running kernel. The rig (dfwb3_rig.c, built -Werror clean; struct-layout cross-checked against the kernel's own disassembly β€” see DF-4266's pack) resolves wb_init from /boot/kernel/kernel via nm(1), fabricates the proven post-attach softc, and dispatches the kernel's own wb_init() β€” its inlined wb_list_rx_init builds the ring exactly as production does at every wb_init call (boot, SIOCSIFFLAGS, every babble reset, watchdog...). The ring that wb_init leaves behind IS the finding's subject.

Baseline β€” stock kernel #0 (Jul 2 2026), run.kld.dmesg.log

  1. Hardware links: all 64 wb_rx_list[i].wb_next == vtophys(&wb_rx_list[0]) (0x2ec4b100 in that boot). A closed ring would link i→i+1 and 63→0. => 64/64 hardware wb_next words point at descriptor 0.
  2. Software links: walk from wb_rx_head via wb_nextdesc: distinct descriptors reachable = 1 / 64 (returned to a visited descriptor after 1 hops).
  3. TX contrast (:878 twin, correct): 128 / 128.
  4. Behaviour: crafted completed frame (OWN=0, RXCMP|FIRST|LAST, len 64) in slot 0, processed by the fixed-OWN comparator (the stock rxeof's inverted loop is DF-4266 and would mask frame processing; the comparator changes ONLY that comparison): frame delivered (delivered=1), then head==chain0:1 head==chain1:0 β€” the ring re-enters descriptor 0. Repairing only the 64 wb_nextdesc words (simulating the one-token fix): the identical state delivers and advances head==chain1:1 β€” the collapse is the only difference.
  5. Chip view: MMIO WB_RXADDR == vtophys(&wb_rx_list[0]) always; the chip fetches desc 0, completes, follows wb_next back to desc 0, finds it unrecycled and suspends β†’ per-frame RX restart (wb_intr:1137, wb_rxeoc:1030-1039 rewrites RXADDR and pokes RXSTART); with DF-4266's inversion also present, the driver additionally re-arms under the chip.

Impact: deterministic RX-throughput collapse / interrupt churn (availability DoS) on W89C840F/Compex RL100-ATX hosts β€” the ring is 1-descriptor deep regardless of traffic; 63/64 pre-armed RX buffers (64 MGETHDR mbufs + 256 KB of contigmalloc buffers) are permanently unreachable. Severity: availability bug (High per DB row; no memory corruption β€” DF-4268 tracks the corruption family and was NOT confirmed, see its pack).

Fix validation β€” kernel #1 (Oct 14 2026, same tree + fix_validated.diff)

fix_validated.diff (combined DF-4267+DF-4268+DF-4270) applied inside the guest's /usr/src; make -j6 nativekernel + installkernel (build log line 18497 recompiles if_wb.c); rebooted into kernel #1; the identical rig re-run (run.kld_fixed.log):

observable stock #0 fixed #1
wb_rx_list[i].wb_next all == &list[0] monotonic i+1 (…b110, …b190, …b210), 63β†’0
software walk from head 1 / 64 64 / 64
head after processing slot 0 chain[0] (stuck) chain[1] (advances)
TX contrast 128/128 128/128 (unchanged)

fix_status: fixed (baseline_reproduced=1, patched_reproduced=0).

Rig artifacts / incidents

None for this scenario (no threads, no races): stock and fixed runs both SURVIVED with the guest clean. The rig bugs recorded in the sibling packs (DF-4268 toggle-thread exit, DF-4270 unguarded txeof dispatch) do not touch the 4267 scenario.

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.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

fixed: on the patched kernel the REAL wb_init builds a true closed ring (64/64 reachable, i+1 links, head advances past slot 0); all baseline defect markers gone

run.kld_fixed.log; fix_validated.diff; VERDICT.md 'Fix validation' table
↓ fix.diffper-fix-DF-4267

Confirmed kernel references

Detail

Evidence (decisive lines)

run.kld.dmesg.log ('=> 64/64 hardware wb_next words point at descriptor 0', 'distinct descriptors reachable = 1 / 64', 'TX contrast ... 128 / 128', '*** DF-4267 CONFIRMED (behavioural) *** ... head==chain1:0', A/B with repaired links 'head==chain1:1'); run.kld_fixed.log (fixed kernel: monotonic wb_next links, '64 / 64', 'head==chain1:1'); VERDICT.md table

PoC changes

Replaced the seed's pure-logic harness verdict ('kernel-level reproduction impossible') with the DF-4266 production-handler KLD dispatch rig: the REAL wb_init() builds the ring over a fabricated post-attach softc; added the fixed-OWN comparator (isolates ring topology from DF-4266's inverted loop), the distinct-descriptor walks, the TX-contrast walk, the one-token A/B link repair, and the RXADDR MMIO check. Fix validation added (combined 3-fix kernel).

Verified recommended fix

nexti = (i == WB_RX_LIST_CNT - 1) ? 0 : i + 1; (mirror the TX-side ternary at if_wb.c:878) β€” validated as fix_validated.diff hunk 1

Verdict

Reproduced on production code: dispatched the running kernel's own wb_init() (its inlined wb_list_rx_init is the defective code, if_wb.c:909-911) from the DF-4266 KLD rig; the production post-init ring shows ALL 64 hardware wb_next words == vtophys(&wb_rx_list[0]) and a software wb_nextdesc walk reaching 1/64 distinct descriptors (TX twin at :878: 128/128, confirming the one-token port typo). Behaviourally, a chip-completed frame processed in slot 0 leaves wb_rx_head AT slot 0 β€” the driver can never advance past descriptor 0, the chip suspends RX after every frame (per-frame receive-engine restart via wb_intr:1137/wb_rxeoc:1030, RXADDR always &list[0]), and 63/64 pre-armed RX buffers are permanently unreachable: deterministic RX-throughput collapse/DoS on W89C840F-equipped hosts. Fix validated on a rebuilt kernel (kernel #1, fix_validated.diff): wb_next links become i+1 with 63->0, software walk 64/64, head advances to chain[1] β€” every defect marker gone.