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

Reply via email to