DF-3641 / trigger.c
/* * DF-3641 โ bnx_sysctl_std_refill() updates bnx_rx_ret_ring[] using * bnx_tx_ringcnt as the loop bound (copy-paste bug) * * Trigger (HARDWARE REQUIRED for the sysctl node to exist; the loop-bound * bug itself is source-provable and was verified on the guest's * byte-identical /usr/src copy): * * sys/dev/netif/bnx/if_bnx.c: * 5664: bnx_sysctl_std_refill(SYSCTL_HANDLER_ARGS) * 5667: struct bnx_rx_ret_ring *ret = &sc->bnx_rx_ret_ring[0]; * 5679: if ((cntmax * sc->bnx_rx_retcnt) >= BGE_STD_RX_RING_CNT/2) * -> EINVAL * 5684: for (i = 0; i < sc->bnx_tx_ringcnt; ++i) <-- WRONG * 5685: sc->bnx_rx_ret_ring[i].bnx_rx_cntmax = cntmax; * * The loop should run over sc->bnx_rx_retcnt (the RX return-ring count); * it runs over bnx_tx_ringcnt. On BCM5719/5720 with rx_retcnt > 1 and * tx_ringcnt == 1 (the common non-bundled RSS setup, e.g. hw.bnx.tx_rings * unset on a 4-RX/1-TX configuration), writing * dev.bnx.0.std_refill=<n> updates ONLY ring 0; rings 1..rx_retcnt-1 keep * the default bnx_rx_cntmax set in bnx_dma_alloc() * (if_bnx.c:4259-4260: (BGE_STD_RX_RING_CNT / 4) / sc->bnx_rx_retcnt), * so the operator-visible "std_refill" value silently disagrees with the * per-ring scheduling thresholds actually in force. * * Not memory-unsafe: tx_ringcnt <= rx_retcnt is structurally guaranteed * (bnx_setup_ring_cnt() if_bnx.c:5784-5799 clamps TX ring_max to * min(BNX_TX_RING_MAX, rx_retcnt) and if_ringcnt_fixup() clamps the * count, sys/net/if.c:3944-3953), so the loop can never run past the * bnx_rx_ret_ring[] allocation; the KASSERT at if_bnx.c:5797 documents * the invariant. Also, 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 becomes 65531, * which makes `bnx_rx_cnt >= bnx_rx_cntmax' (if_bnx.c:2825) almost * never fire: refill scheduling from that path stops (RX eventually * still refills via the bnx_rx_std_used >= 256 check at if_bnx.c:2890). * Root-only sysctl; performance/correctness only. * * The audit QEMU guest has no Broadcom NIC, so dev.bnx.0.std_refill does * not exist there (run.log: sysctl hw.bnx unknown oid). */ #include <sys/types.h> #include <sys/sysctl.h> #include <stdio.h> int main(void) { size_t len; int v; len = sizeof(v); if (sysctlbyname("dev.bnx.0.std_refill", &v, &len, NULL, 0) < 0) { fprintf(stderr, "DF-3641: dev.bnx.0.std_refill absent " "(no bnx hardware) โ as expected on the audit guest.\n"); return 1; } /* On hardware with rx_retcnt > tx_ringcnt this write only reaches * rings [0, tx_ringcnt); read back `dev.bnx.0.rx_rings`, * `dev.bnx.0.tx_rings`, and per-ring behavior to observe the * mismatch. */ fprintf(stderr, "std_refill=%d โ compare rx_rings/tx_rings counts; " "rings [%d..%d) keep the old cntmax\n", v, 1, v); return 0; } |