This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit be559984e549e1778afb5ab97f041376e42b4fdb Author: Jorge Guzman <[email protected]> AuthorDate: Thu Aug 6 16:00:20 2026 -0300 stm32h7/ethernet: never transmit a reply into a full TX ring The receive path hands incoming packets to the stack and transmits whatever reply comes back, without checking that a TX descriptor is free, though the poll path checks exactly that. Under sustained bidirectional load the reply lands on a descriptor the DMA still owns: with assertions built in, a panic from the RX work queue (DEBUGASSERT(des3 & RD_OWN), reproduced under a VNC pointer flood); without them, corruption of a frame in flight. A reply to received data is almost always an acknowledgement, and a peer that misses one retransmits; overwriting a frame the DMA owns recovers from nothing. Drop the reply when the ring is full, using the same descriptor test the poll path already trusts. Assisted-by: Claude:opus-5 Signed-off-by: Jorge Guzman <[email protected]> --- arch/arm/src/stm32h7/stm32_ethernet.c | 55 +++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/arch/arm/src/stm32h7/stm32_ethernet.c b/arch/arm/src/stm32h7/stm32_ethernet.c index 26bba57c59b..bdf09e82f65 100644 --- a/arch/arm/src/stm32h7/stm32_ethernet.c +++ b/arch/arm/src/stm32h7/stm32_ethernet.c @@ -1149,6 +1149,17 @@ static struct eth_desc_s *stm32_get_next_txdesc(struct stm32_ethmac_s *priv, * ****************************************************************************/ +static bool stm32_txringfull(FAR struct stm32_ethmac_s *priv) +{ + /* The ring is full when the head descriptor still belongs to the DMA. + * Transmitting into it anyway corrupts a frame in flight; with + * assertions built in it panics from the RX work queue instead. + */ + + return (priv->txhead->des3 & ETH_TDES3_RD_OWN) != 0 || + priv->txhead->des0 != 0; +} + static int stm32_transmit(struct stm32_ethmac_s *priv) { struct eth_desc_s *txdesc; @@ -1990,9 +2001,20 @@ static void stm32_receive(struct stm32_ethmac_s *priv) if (priv->dev.d_len > 0) { - /* And send the packet */ + /* Send the reply, unless the TX ring is full. The reply + * to received data is almost always an acknowledgement, and + * a peer that misses one retransmits; overwriting a frame + * the DMA still owns recovers from nothing. + */ - stm32_transmit(priv); + if (!stm32_txringfull(priv)) + { + stm32_transmit(priv); + } + else + { + priv->dev.d_len = 0; + } } } else @@ -2013,9 +2035,20 @@ static void stm32_receive(struct stm32_ethmac_s *priv) if (priv->dev.d_len > 0) { - /* And send the packet */ + /* Send the reply, unless the TX ring is full. The reply + * to received data is almost always an acknowledgement, and + * a peer that misses one retransmits; overwriting a frame + * the DMA still owns recovers from nothing. + */ - stm32_transmit(priv); + if (!stm32_txringfull(priv)) + { + stm32_transmit(priv); + } + else + { + priv->dev.d_len = 0; + } } } else @@ -2036,7 +2069,19 @@ static void stm32_receive(struct stm32_ethmac_s *priv) if (priv->dev.d_len > 0) { - stm32_transmit(priv); + /* Send the reply, unless the TX ring is full. A peer + * that misses an ARP reply asks again; overwriting a + * frame the DMA still owns recovers from nothing. + */ + + if (!stm32_txringfull(priv)) + { + stm32_transmit(priv); + } + else + { + priv->dev.d_len = 0; + } } } else
