bnx_sysctl_std_refill iterates bnx_tx_ringcnt while indexing bnx_rx_ret_ring[] (copy-paste): only min(tx,rx) rings updated; validation admits negative cntmax that wraps uint16_t
Summary
The dev.bnx.N.std_refill handler loops 'for (i = 0; i < sc->bnx_tx_ringcnt; ++i) sc->bnx_rx_ret_ring[i].bnx_rx_cntmax = cntmax;' - the bound is copied from a TX loop and must be sc->bnx_rx_retcnt. With rx_retcnt>1 and tx_ringcnt==1 (common BCM5719/5720 non-bundled RSS setup), only ring 0 is updated; rings 1..rx_retcnt-1 silently keep the default, so the operator-visible value disagrees with the per-ring thresholds actually used. Not memory-unsafe: tx_ringcnt <= rx_retcnt structurally guaranteed. The validation also admits negative cntmax which truncates into uint16_t bnx_rx_cntmax (e.g. -5 -> 65531), effectively disabling that refill trigger (RX still refills via the independent used>=256 check). Root-only sysctl; correctness/performance only.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3641 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.6 KB | β raw | |
| VERDICT.md | β | 3.5 KB | β raw | |
| trigger.c | β | 2.9 KB | view raw | |
| build.sh | β | 117 B | view raw | |
| run.sh | β | 245 B | view raw | |
| build.log | β | 74 B | view raw | |
| run.log | β | 1.6 KB | view raw | |
| env.txt | β | 928 B | view raw | |
| fix.diff | β | 461 B | view raw | |
| verdict.json | β | 3.8 KB | view raw |
DF-3641 β bnx_sysctl_std_refill tx/rx ring-count copy-paste β evidence pack
What: the handler for dev.bnx.N.std_refill
(sys/dev/netif/bnx/if_bnx.c:5684-5685) runs
for (i = 0; i < sc->bnx_tx_ringcnt; ++i) sc->bnx_rx_ret_ring[i].bnx_rx_cntmax = cntmax;
β the loop bound is a copy-paste from a TX loop and must be
sc->bnx_rx_retcnt. With rx_retcnt > 1, tx_ringcnt == 1 (common BCM5719/
5720 RSS topology) only ring 0 is updated. The validation at 5679 also
admits negative cntmax, which truncates into the uint16_t field.
Impact: configuration correctness only β tx_ringcnt <= rx_retcnt is
structurally guaranteed (if_bnx.c:5784-5799 + sys/net/if.c:3944-3953), so
there is no OOB; root-only sysctl. Filed Info / base:dev.
Reproduce (audit guest β honest negative):
cc -O -Wall -o trigger trigger.c # guest; builds clean ./run.sh # via dfbsd-qemu/vm.sh run_root
run.log: dev.bnx.0.std_refill β unknown oid (no Broadcom hardware), plus a
verbatim guest-side dump of lines 5679-5686 showing the wrong bound; src md5
matches the audited tree β runtime verdict inconclusive (source-level
certainty).
On real hardware: sysctl dev.bnx.0.rx_rings=4-style setup with 1 TX
ring, write dev.bnx.0.std_refill=64 β rings 1..3 keep the default
(512/4)/4 = 32.
Fix: fix.diff β iterate bnx_rx_retcnt, reject cntmax < 1
(git-apply verified).
Files: VERDICT.md (full narrative + structural tx<=rx proof), trigger.c, build.sh/run.sh, build.log/run.log (untrimmed guest transcripts), env.txt, manifest.json, verdict.json.
DF-3641 β bnx_sysctl_std_refill() iterates bnx_tx_ringcnt while indexing bnx_rx_ret_ring[]
File: sys/dev/netif/bnx/if_bnx.c (pass 2, GLM 5.3)
Class: copy-paste loop-bound defect (CWE-193-adjacent; here correctness only)
Severity filed: Info, bucket base:dev, confidence certain (source fact)
Verdict: inconclusive at runtime β sysctl node requires attached bnx
hardware, absent on the audit guest.
1. Root cause (path:line)
bnx_sysctl_std_refill() β the handler for dev.bnx.N.std_refill:
sys/dev/netif/bnx/if_bnx.c:5669β readsret = &sc->bnx_rx_ret_ring[0](an RX return ring)sys/dev/netif/bnx/if_bnx.c:5679β validates(cntmax * sc->bnx_rx_retcnt) >= BGE_STD_RX_RING_CNT / 2β EINVALsys/dev/netif/bnx/if_bnx.c:5684βfor (i = 0; i < sc->bnx_tx_ringcnt; ++i)β wrong countsys/dev/netif/bnx/if_bnx.c:5685βsc->bnx_rx_ret_ring[i].bnx_rx_cntmax = cntmax;
The loop must run over sc->bnx_rx_retcnt. On the common BCM5719/5720 RSS
setup with rx_retcnt > 1 and tx_ringcnt == 1 (the non-bundled topology;
bnx_setup_ring_cnt() allows tx counts from 1..min(4, rx_retcnt)), a write to
dev.bnx.N.std_refill updates only ring 0 β rings 1..rx_retcnt-1 silently
keep the default bnx_rx_cntmax assigned in bnx_dma_alloc()
(if_bnx.c:4259-4260: (BGE_STD_RX_RING_CNT / 4) / sc->bnx_rx_retcnt), so
the operator-visible value disagrees with the per-ring refill thresholds
actually in force (used at if_bnx.c:2825, ret->bnx_rx_cnt >=
ret->bnx_rx_cntmax).
2. Not memory-unsafe (why Info)
tx_ringcnt <= rx_retcnt is structurally guaranteed before any of this code
runs: bnx_setup_ring_cnt() (if_bnx.c:5784-5799) clamps the TX ring map to
min(BNX_TX_RING_MAX, rx_retcnt) and if_ringcnt_fixup()
(sys/net/if.c:3944-3953) clamps the count; the KASSERT at if_bnx.c:5797
documents the invariant (if_ringmap_align() only equalizes CPU grids, not
counts β verified in sys/net/if.c:4029-4036). The loop therefore can never
run past the bnx_rx_ret_ring[] allocation.
Related wart (also fixed in fix.diff): the validation at 5679 admits negative
cntmax (n * retcnt is negative, never >= 256), which then truncates into
the uint16_t bnx_rx_cntmax (if_bnxvar.h:204) β e.g. β5 β 65531, making
the bnx_rx_cnt >= bnx_rx_cntmax test at 2825 almost never fire (refill
scheduling from that path stops; RX still refills via the independent
bnx_rx_std_used >= BGE_STD_RX_RING_CNT/2 check at 2890). Root-only sysctl,
performance/correctness only.
3. Guest evidence (why inconclusive)
run.log (guest, byte-identical source):
- pci/if_bnx in kernel; NO_BROADCOM_DEVICE; ifconfig -l = vtnet0 lo0;
sysctl dev.bnx.0.std_refill β unknown oid β the handler never executes
on this guest.
- Guest-side dump of lines 5679-5686 shows the mismatch verbatim.
- md5 if_bnx.c == audited tree.
trigger.c probes dev.bnx.0.std_refill via sysctlbyname and correctly
reports it absent on the guest β honest inconclusive / reproduced: 0.
4. Fix
fix.diff β iterate sc->bnx_rx_retcnt and reject cntmax < 1.
git-apply-able (verified). Not runtime-validatable on this guest
(fix_status: not_testable).
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_testablefix.diff corrects the loop bound to sc->bnx_rx_retcnt and adds the cntmax<1 rejection; git apply --check passes against the audited tree. Cannot be runtime-validated: the sysctl handler never executes on the audit guest (no Broadcom device), so neither baseline nor patched behavior is observable there.
fix.diff; run.log (dev.bnx.0.std_refill unknown oid => handler unreachable on guest)
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log (guest: sysctl dev.bnx.0.std_refill unknown oid, NO_BROADCOM_DEVICE, md5 match, verbatim sed dump of the wrong loop bound), VERDICT.md (path:line trace + the structural tx<=rx proof), trigger.c (sysctlbyname probe, builds clean -O -Wall), fix.diff (iterate bnx_rx_retcnt + reject cntmax<1, git-apply verified)
PoC changes
No seed existed (new pass-2 finding); trigger written as a sysctl probe that self-reports the absent OID on the audit guest.
Verified recommended fix
In bnx_sysctl_std_refill, iterate sc->bnx_rx_retcnt (not bnx_tx_ringcnt) and reject cntmax < 1 (see fix.diff).
Verdict
Source-level certain: bnx_sysctl_std_refill (sys/dev/netif/bnx/if_bnx.c:5684-5685) iterates sc->bnx_tx_ringcnt while indexing sc->bnx_rx_ret_ring[i] β a copy-paste of the loop bound from a TX loop; it must iterate sc->bnx_rx_retcnt. On BCM5719/5720 with rx_retcnt>1 and tx_ringcnt==1 (common non-bundled RSS setup), writing dev.bnx.N.std_refill updates only ring 0; rings 1..rx_retcnt-1 silently keep the default bnx_rx_cntmax from bnx_dma_alloc (if_bnx.c:4259-4260), so the operator-visible value disagrees with the per-ring refill thresholds in force at if_bnx.c:2825. Not memory-unsafe: tx_ringcnt <= rx_retcnt is structurally guaranteed (if_bnx.c:5784-5799 clamps TX ring_max to min(BNX_TX_RING_MAX, rx_retcnt); if_ringcnt_fixup clamps the count, sys/net/if.c:3944-3953; if_ringmap_align only equalizes grids, sys/net/if.c:4029-4036). Also fixed: validation at 5679 admits negative cntmax, which truncates into uint16_t bnx_rx_cntmax (-5 -> 65531, effectively disabling that refill trigger; RX still refills via the bnx_rx_std_used>=256 check at 2890). Runtime verification honestly inconclusive: sysctl dev.bnx.0.std_refill is unknown oid on the audit guest (no Broadcom device, NO_BROADCOM_DEVICE), so the handler cannot execute there; guest /usr/src if_bnx.c md5 49a18c4b7642b9b2ebd1d02ad11542e4 matches the audited tree and the guest-side dump of lines 5679-5686 shows the mismatch verbatim.
No comments yet.