netmap_sw_to_nic() double-increments k1: skipped TX rings and latent kring array OOB (currently dead code β no d_poll wired)
Summary
The forwarding loop advances k1 twice per iteration (for-increment plus a stray 'k1++; // XXX why?'), so iteration i processes tx_rings[2i]: half the TX rings never receive forwarded host buffers, and k1 reaches tx_rings + 2*(num_tx_rings-1) - past the krings allocation whenever ntx >= nrx+2 (heap OOB kring deref) and into the rx krings otherwise. Unreachable today: the only caller is netmap_poll() and netmap_cdevsw wires no d_poll; the subsystem also does not compile on current DragonFly. Filed Info: becomes live the moment d_poll is wired or on FreeBSD-lineage builds. Fix: one deleted line.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3335 Β· 3 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | doc | summary | 1.4 KB | β raw |
| VERDICT.md | verdict | arithmetic + reachability | 1.7 KB | β raw |
| fix.diff | suggested-fix | delete stray k1++ | 224 B | view raw |
DF-3335 β netmap_sw_to_nic() double-increments k1: skipped rings / latent kring OOB
Target: sys/net/netmap/netmap.c:798-824 (netmap_sw_to_nic).
The for loop header already advances k1 (i++, k1++, netmap.c:798);
line 823 increments it again inside the body (k1++; // XXX why?).
Net effect: iteration i processes tx_rings[2*i] β every second TX ring
is silently skipped, and k1 runs to tx_rings + 2*(num_tx_rings-1),
i.e. past the end of the krings allocation whenever
2*(num_tx_rings-1) >= num_tx_rings + num_rx_rings (ntx >= nrx+2): the
k1->ring / k1->ring->avail accesses are then heap out-of-bounds kring
type-confusion. For realistic NICs (ntx <= nrx+1) k1 stays inside the
combined allocation but dereferences rx krings as tx (wrong-ring buffer
swapping between the host RX ring and an RX ring).
Currently dead code on this platform: netmap_sw_to_nic is called only
from netmap_poll() (netmap.c:1797), and netmap_cdevsw has no d_poll
entry (netmap_freebsd.c:397-404) β netmap_poll itself is unreachable, so
neither the skip bug nor the OOB can execute on a stock DragonFly. It
becomes live the moment d_poll is wired (or on FreeBSD-lineage builds where
poll exists). Filed Info for that reason; the fix is one deleted line.
Verification: skipped (Info severity, dead code). Analysis-verified by reading the loop arithmetic; fix.diff deletes the stray increment.
DF-3335 β VERDICT (analysis-verified; no runtime PoC β dead code)
Loop arithmetic: k1 = &tx_rings[0] before the loop (netmap.c:783); each
iteration executes k1++ at netmap.c:823 AND the for-increment k1++
(netmap.c:798) -> k1 advances 2 per iteration, so iteration i touches
tx_rings[2i]. Consequences:
- odd TX rings never receive forwarded buffers (functional bug);
- last touched index = 2*(num_tx_rings-1); the krings allocation is
(ntx+1 + nrx+1) entries (netmap_krings_create, netmap.c:416-423), so for
ntx >= nrx+2 the accesses
k1->nkr_num_slots,k1->ring,k1->ring->avail(netmap.c:800-801, 804, 819-820) read/write past the allocation (heap OOB kring deref; k1->ring is a pointer loaded OOB -> subsequent slot writes through it are wild); for typical ntx<=nrx+1 k1 aliases rx krings as tx (in-allocation type confusion, buffer-index swapping into rx rings).
Reachability: ONLY from netmap_poll() (netmap.c:1797) in transparent mode (netmap_fwd sysctl or user-settable ring->flags NR_FORWARD). netmap_poll is unreachable on DragonFly: netmap_cdevsw (netmap_freebsd.c:397-404) has no .d_poll entry, so dev_dpoll falls through to the default. Verified by grep: no other caller exists. Additionally the whole netmap subsystem does not compile on current DragonFly (if_unused7 bit-rot, see DF-3332/env.txt).
Status: Info/likely, dead code today, one-line fix.
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
not_testablenot testable: function is dead code (no d_poll in cdevsw) and the subsystem does not compile on this tree
fix.diff (one deleted line)
Confirmed kernel references
Detail
Evidence (decisive lines)
['VERDICT.md: loop-arithmetic derivation + reachability analysis', 'fix.diff: deletes the stray increment']
PoC changes
not built (Info, dead code)
Verified recommended fix
delete the stray k1++ at netmap.c:823
Verdict
netmap_sw_to_nic() advances k1 twice per loop iteration (for-increment at netmap.c:798 plus the stray 'k1++; // XXX why?' at netmap.c:823), so iteration i processes tx_rings[2i]: half the TX rings are skipped and k1 runs to tx_rings+2*(num_tx_rings-1) β past the krings allocation for ntx >= nrx+2 (heap-OOB kring deref / pointer-load) and into rx krings otherwise (wrong-ring buffer swapping). DEAD CODE on this platform: the only caller is netmap_poll() (netmap.c:1797) and netmap_cdevsw wires no d_poll (netmap_freebsd.c:397-404); the whole subsystem additionally does not compile on current DragonFly. Info severity; one-line fix; Phase V skipped.
No comments yet.