* [RESEND PATCH net v3 0/2] stmmac crash/stall fixes when under memory pressure
@ 2026-03-28 19:25 Sam Edwards
2026-03-28 19:25 ` [RESEND PATCH net v3 1/2] net: stmmac: Prevent NULL deref when RX memory exhausted Sam Edwards
2026-03-28 19:25 ` [RESEND PATCH net v3 2/2] net: stmmac: Prevent indefinite RX stall on buffer exhaustion Sam Edwards
0 siblings, 2 replies; 3+ messages in thread
From: Sam Edwards @ 2026-03-28 19:25 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Maxime Coquelin, Alexandre Torgue, Russell King (Oracle),
Maxime Chevallier, Ovidiu Panait, Vladimir Oltean, Baruch Siach,
Serge Semin, Giuseppe Cavallaro, netdev, linux-stm32,
linux-arm-kernel, linux-kernel, Sam Edwards
Hi netdev,
This is v3 of my series containing a pair of bugfixes for the stmmac driver's
receive pipeline. These issues occur when stmmac_rx_refill() does not (fully)
succeed, which happens more frequently when free memory is low.
The first patch closes Bugzilla bug #221010 [1], where stmmac_rx() can circle
around to a still-dirty descriptor (with a NULL buffer pointer), mistake it for
a filled descriptor (due to OWN=0), and attempt to dereference the buffer.
In testing that patch, I discovered a second issue: starvation of available RX
buffers causes the NIC to stop sending interrupts; if the driver stops polling,
it will wait indefinitely for an interrupt that will never come. (Note: the
first patch makes this issue more prominent -- mostly because it lets the
system survive long enough to exhibit it -- but doesn't *cause* it.) The second
patch addresses that problem as well.
Both patches are minimal, appropriate for stable, and designated to `net`. My
focus is on small, obviously-correct, easy-to-explain changes: I'll follow up
with another patch/series (something like [2]) for `net-next` that fixes the
ring in a more robust way.
The tx and zc paths seem to have similar low-memory bugs, to be addressed in
separate series.
Regards,
Sam
---
RESEND: Oops, my subject line didn't have `net`, so I'm resending the series to
fix that.
[1] https://bugzilla.kernel.org/show_bug.cgi?id=221010
[2] https://lore.kernel.org/netdev/20260316021009.262358-4-CFSworks@gmail.com/
v3:
- Rebased on latest net/main
- Changed patch 2 to require that stmmac_rx_refill() *fully* succeeds before
exiting polling, to reduce the chance of rx drops.
- DID NOT use the CIRC_SPACE() macro as suggested by Russell: I fear that the
perspective shift (first think of the dirty descriptors as the "work" that
refill "consumes" -- therefore the "space" is how much stmmac_rx() may loop)
is too counterintuitive for a stable fix, but I'll do it in v4 if reviewers
insist.
- Updated the recipients for the series, which was invalidated in v2 due to the
`Fixes:`
v2: https://lore.kernel.org/netdev/20260319184031.8596-1-CFSworks@gmail.com/T/
- Completely rewrote the commit message of patch 1, now assuming the reader is
generally familiar with DMA but wholly unfamiliar with the stmmac device
(thanks Jakub!)
- Added missing `Fixes:` to patch 2
- Moved patch 2's `int budget = limit;` decl per the reverse-xmas-tree rule
- Dropped patch 3: this was a code improvement not appropriate for stable
- Generated the series with --subject-prefix='PATCH net'
v1: https://lore.kernel.org/netdev/20260316021009.262358-1-CFSworks@gmail.com/
Sam Edwards (2):
net: stmmac: Prevent NULL deref when RX memory exhausted
net: stmmac: Prevent indefinite RX stall on buffer exhaustion
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
--
2.52.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [RESEND PATCH net v3 1/2] net: stmmac: Prevent NULL deref when RX memory exhausted
2026-03-28 19:25 [RESEND PATCH net v3 0/2] stmmac crash/stall fixes when under memory pressure Sam Edwards
@ 2026-03-28 19:25 ` Sam Edwards
2026-03-28 19:25 ` [RESEND PATCH net v3 2/2] net: stmmac: Prevent indefinite RX stall on buffer exhaustion Sam Edwards
1 sibling, 0 replies; 3+ messages in thread
From: Sam Edwards @ 2026-03-28 19:25 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Maxime Coquelin, Alexandre Torgue, Russell King (Oracle),
Maxime Chevallier, Ovidiu Panait, Vladimir Oltean, Baruch Siach,
Serge Semin, Giuseppe Cavallaro, netdev, linux-stm32,
linux-arm-kernel, linux-kernel, Sam Edwards, stable
The CPU receives frames from the MAC through conventional DMA: the CPU
allocates buffers for the MAC, then the MAC fills them and returns
ownership to the CPU. For each hardware RX queue, the CPU and MAC
coordinate through a shared ring array of DMA descriptors: one
descriptor per DMA buffer. Each descriptor includes the buffer's
physical address and a status flag ("OWN") indicating which side owns
the buffer: OWN=0 for CPU, OWN=1 for MAC. The CPU is only allowed to set
the flag and the MAC is only allowed to clear it, and both must move
through the ring in sequence: thus the ring is used for both
"submissions" and "completions."
In the stmmac driver, stmmac_rx() bookmarks its position in the ring
with the `cur_rx` index. The main receive loop in that function checks
for rx_descs[cur_rx].own=0, gives the corresponding buffer to the
network stack (NULLing the pointer), and increments `cur_rx` modulo the
ring size. After the loop exits, stmmac_rx_refill(), which bookmarks its
position with `dirty_rx`, allocates fresh buffers and rearms the
descriptors (setting OWN=1). If it fails any allocation, it simply stops
early (leaving OWN=0) and will retry where it left off when next called.
This means descriptors have a three-stage lifecycle (terms my own):
- `empty` (OWN=1, buffer valid)
- `full` (OWN=0, buffer valid and populated)
- `dirty` (OWN=0, buffer NULL)
But because stmmac_rx() only checks OWN, it confuses `full`/`dirty`. In
the past (see 'Fixes:'), there was a bug where the loop could cycle
`cur_rx` all the way back to the first descriptor it dirtied, resulting
in a NULL dereference when mistaken for `full`. The aforementioned
commit resolved that *specific* failure by capping the loop's iteration
limit at `dma_rx_size - 1`, but this is only a partial fix: if the
previous stmmac_rx_refill() didn't complete, then there are leftover
`dirty` descriptors that the loop might encounter without needing to
cycle fully around. The current code therefore panics (see 'Closes:')
when stmmac_rx_refill() is memory-starved long enough for `cur_rx` to
catch up to `dirty_rx`.
Fix this by further tightening the clamp from `dma_rx_size - 1` to
`dma_rx_size - stmmac_rx_dirty() - 1`, subtracting any remnant dirty
entries and limiting the loop so that `cur_rx` cannot catch back up to
`dirty_rx`. This carries no risk of arithmetic underflow: since the
maximum possible return value of stmmac_rx_dirty() is `dma_rx_size - 1`,
the worst the clamp can do is prevent the loop from running at all.
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 <CFSworks@gmail.com>
---
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
^ permalink raw reply related [flat|nested] 3+ messages in thread* [RESEND PATCH net v3 2/2] net: stmmac: Prevent indefinite RX stall on buffer exhaustion
2026-03-28 19:25 [RESEND PATCH net v3 0/2] stmmac crash/stall fixes when under memory pressure Sam Edwards
2026-03-28 19:25 ` [RESEND PATCH net v3 1/2] net: stmmac: Prevent NULL deref when RX memory exhausted Sam Edwards
@ 2026-03-28 19:25 ` Sam Edwards
1 sibling, 0 replies; 3+ messages in thread
From: Sam Edwards @ 2026-03-28 19:25 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni
Cc: Maxime Coquelin, Alexandre Torgue, Russell King (Oracle),
Maxime Chevallier, Ovidiu Panait, Vladimir Oltean, Baruch Siach,
Serge Semin, Giuseppe Cavallaro, netdev, linux-stm32,
linux-arm-kernel, linux-kernel, Sam Edwards, stable
The stmmac driver handles interrupts in the usual NAPI way: an interrupt
arrives, the NAPI instance is scheduled and interrupts are masked, and
the actual work occurs in the NAPI polling function. Once no further
work remains, interrupts are unmasked and the NAPI instance is put to
sleep to await a future interrupt. In the receive case, the MAC only
sends the interrupt when a DMA operation completes; thus the driver must
make sure a usable RX DMA descriptor exists before expecting a future
interrupt.
The main receive loop in stmmac_rx() exits under one of 3 conditions:
1) It encounters a DMA descriptor with OWN=1, indicating that no further
pending data exists. The MAC will use this descriptor for the next
RX DMA operation, so the driver can expect a future interrupt.
2) It exhausts the NAPI budget. In this case, the driver doesn't know
whether the MAC has any usable DMA descriptors. But when the driver
consumes its full budget, that signals NAPI to keep polling, so the
question is moot.
3) It runs out of (non-dirty) descriptors in the RX ring. In this case,
the MAC will only have a usable descriptor if stmmac_rx_refill()
succeeds (at least partially).
Currently, stmmac_rx() lacks any check against scenario #3 and
stmmac_rx_refill() failing: it will stop NAPI polling and unmask
interrupts to await an interrupt that will never arrive, stalling the
receive pipeline indefinitely.
Fix this by checking stmmac_rx_dirty(): it will return 0 if
stmmac_rx_refill() fully succeeded and we can safely await an interrupt.
Any nonzero value means some allocations failed, in which case we risk
dropping frames if a large traffic burst exhausts the surviving
non-dirties. Therefore, simply return the full budget (to keep polling)
until all allocations succeed.
Fixes: 47dd7a540b8a ("net: add support for STMicroelectronics Ethernet controllers.")
Cc: stable@vger.kernel.org
Signed-off-by: Sam Edwards <CFSworks@gmail.com>
---
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index f98b070073c0..81f764352f3d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -5604,6 +5604,7 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
unsigned int desc_size;
struct sk_buff *skb = NULL;
struct stmmac_xdp_buff ctx;
+ int budget = limit;
int xdp_status = 0;
int bufsz;
@@ -5870,6 +5871,10 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue)
priv->xstats.rx_dropped += rx_dropped;
priv->xstats.rx_errors += rx_errors;
+ /* If stmmac_rx_refill() failed, keep trying until it doesn't. */
+ if (unlikely(stmmac_rx_dirty(priv, queue) > 0))
+ return budget;
+
return count;
}
--
2.52.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-03-28 19:25 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-28 19:25 [RESEND PATCH net v3 0/2] stmmac crash/stall fixes when under memory pressure Sam Edwards
2026-03-28 19:25 ` [RESEND PATCH net v3 1/2] net: stmmac: Prevent NULL deref when RX memory exhausted Sam Edwards
2026-03-28 19:25 ` [RESEND PATCH net v3 2/2] net: stmmac: Prevent indefinite RX stall on buffer exhaustion Sam Edwards
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox