Under typical conditions, `stmmac_rx_refill()` rearms every descriptor in the RX ring. However, if it fails to allocate memory, it will stop early and try again the next time it's called. In this situation, it (correctly) leaves OWN=0 because the hardware is not yet allowed to reclaim the descriptor. `stmmac_rx()`, however, does not anticipate this scenario: it assumes `cur_rx` always points to a valid descriptor, and that OWN=0 means the buffer is ready for the driver. A `min()` clamp at the start prevents `cur_rx` from wrapping all the way around the buffer (see Fixes:), apparently intended to prevent the "head=tail ambiguity problem" from breaking `stmmac_rx_refill()`. But this safeguard is incomplete because the threshold to stay behind is actually `dirty_rx`, not `cur_rx`. It works most of the time only by coincidence: `stmmac_rx_refill()` usually succeeds often enough that it leaves `dirty_rx == cur_rx`. But when `stmmac_rx()` is called when `dirty_rx != cur_rx` and the NAPI budget is high, `cur_rx` can advance to a still-dirty descriptor, violating the invariant and triggering a panic when the driver attempts to access a missing buffer. This can easily be fixed by subtracting `stmmac_rx_dirty()` from the clamp. Because that function currently interprets `dirty_rx == cur_rx` to mean "none dirty," its maximum return value is `dma_rx_size - 1`, so doing this carries no risk of underflow, though does (like the Fixes:) leave a clean buffer unreachable. Fixes: b6cb4541853c7 ("net: stmmac: avoid rx queue overrun") Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221010 Cc: stable@vger.kernel.org Signed-off-by: Sam Edwards --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 6827c99bde8c..f98b070073c0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -5609,7 +5609,8 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) dma_dir = page_pool_get_dma_dir(rx_q->page_pool); bufsz = DIV_ROUND_UP(priv->dma_conf.dma_buf_sz, PAGE_SIZE) * PAGE_SIZE; - limit = min(priv->dma_conf.dma_rx_size - 1, (unsigned int)limit); + limit = min(priv->dma_conf.dma_rx_size - stmmac_rx_dirty(priv, queue) - 1, + (unsigned int)limit); if (netif_msg_rx_status(priv)) { void *rx_head; -- 2.52.0