STM 32F4xx - is there any buffer overflow protection on incoming ETH packets? - Page 1
Forum section: electronics / general

author: peter-h
author_rank: Super Contributor
timestamp: August 05, 2025, 10:53:08 am
content: These should be limited to the MTU, which is normally about 1500 bytes, by convention. The ETH subsystem, AIUI from Synopsis, has its own DMA controller. What stops a malformed packet of say 100k bytes trashing a chunk of RAM?Obviously they cannot just transfer x bytes where x is the packet length. There must be a separate upper bound somewhere.

author: wek
author_rank: Frequent Contributor
timestamp: August 05, 2025, 11:30:22 am
content: > What stops a malformed packet of say 100k bytes trashing a chunk of RAM?The DMA for Rx works out of the descriptors for individual memory chunks, besides a couple of dedicated registers. In each descriptor, the memory size it can fill is given in RBS field of the RDES1: Receive descriptor Word1 (it's a bit more complicated than that, but that's the gist). So that prevents thrashing of RAM.One level before that, the MAC receiving data if it runs out of FIFO (2kB RAM internal to the ETH) and DMA is not able to store the received data for whatever reason to the mcu's SRAM, it simply throws an overflow error and that gets stored both into the currently used descriptor and into global variables. Also, the MAC cuts off frames longer than 2048B (default)/16kB due to a mechanism they call watchdog.JW

author: peter-h
author_rank: Super Contributor
timestamp: August 05, 2025, 01:12:40 pm
content: So you could get up to 16k bytes?

author: wek
author_rank: Frequent Contributor
timestamp: August 06, 2025, 07:25:13 am
content: Never tried.

author: peter-h
author_rank: Super Contributor
timestamp: August 06, 2025, 08:01:05 am
content: What I am getting at is that a malformed packet could be up to this size, but nobody is going to be allocating RAM for that - especially as normally one allocates something like four packet buffers, to get any meaningful performance.But maybe I misunderstand and the value in Receive descriptor Word1 is a real packet size limit. It would be really interesting what value ST's ETH code loads in there. I had a dig around and it looks like 1524 is the maxCode:/** @defgroup ETH_Buffers_setting ETH Buffers setting* @{*/#define ETH_MAX_PACKET_SIZE 1524U /*!< ETH_HEADER + ETH_EXTRA + ETH_VLAN_TAG + ETH_MAX_ETH_PAYLOAD + ETH_CRC */#define ETH_HEADER 14U /*!< 6 byte Dest addr, 6 byte Src addr, 2 byte length/type */#define ETH_CRC 4U /*!< Ethernet CRC */#define ETH_EXTRA 2U /*!< Extra bytes in some cases */#define ETH_VLAN_TAG 4U /*!< optional 802.1q VLAN Tag */#define ETH_MIN_ETH_PAYLOAD 46U /*!< Minimum Ethernet payload size */#define ETH_MAX_ETH_PAYLOAD 1500U /*!< Maximum Ethernet payload size */#define ETH_JUMBO_FRAME_PAYLOAD 9000U /*!< Jumbo frame payload size *//* Ethernet driver receive buffers are organized in a chained linked-list, whenan ethernet packet is received, the Rx-DMA will transfer the packet from RxFIFOto the driver receive buffers memory.Depending on the size of the received ethernet packet and the size ofeach ethernet driver receive buffer, the received packet can take one or moreethernet driver receive buffer.In below are defined the size of one ethernet driver receive buffer ETH_RX_BUF_SIZEand the total count of the driver receive buffers ETH_RXBUFNB.The configured value for ETH_RX_BUF_SIZE and ETH_RXBUFNB are only provided asexample, they can be reconfigured in the application layer to fit the applicationneeds *//* Here we configure each Ethernet driver receive buffer to fit the Max size Ethernetpacket */#ifndef ETH_RX_BUF_SIZE#define ETH_RX_BUF_SIZE ETH_MAX_PACKET_SIZE#endif/* 5 Ethernet driver receive buffers are used (in a chained linked list)*/#ifndef ETH_RXBUFNB#define ETH_RXBUFNB 5U /* 5 Rx buffers of size ETH_RX_BUF_SIZE */#endif/* Ethernet driver transmit buffers are organized in a chained linked-list, whenan ethernet packet is transmitted, Tx-DMA will transfer the packet from thedriver transmit buffers memory to the TxFIFO.Depending on the size of the Ethernet packet to be transmitted and the size ofeach ethernet driver transmit buffer, the packet to be transmitted can takeone or more ethernet driver transmit buffer.In below are defined the size of one ethernet driver transmit buffer ETH_TX_BUF_SIZEand the total count of the driver transmit buffers ETH_TXBUFNB.The configured value for ETH_TX_BUF_SIZE and ETH_TXBUFNB are only provided asexample, they can be reconfigured in the application layer to fit the applicationneeds *//* Here we configure each Ethernet driver transmit buffer to fit the Max size Ethernetpacket */#ifndef ETH_TX_BUF_SIZE#define ETH_TX_BUF_SIZE ETH_MAX_PACKET_SIZE#endif/* 5 ethernet driver transmit buffers are used (in a chained linked list)*/#ifndef ETH_TXBUFNB#define ETH_TXBUFNB 5U /* 5 Tx buffers of size ETH_TX_BUF_SIZE */#endif/*** @}*//** @defgroup ETH_DMA_TX_Descriptor ETH DMA TX Descriptor* @{*//*DMA Tx Descriptor-----------------------------------------------------------------------------------------------TDES0 | OWN(31) | CTRL[30:26] | Reserved[25:24] | CTRL[23:20] | Reserved[19:17] | Status[16:0] |-----------------------------------------------------------------------------------------------TDES1 | Reserved[31:29] | Buffer2 ByteCount[28:16] | Reserved[15:13] | Buffer1 ByteCount[12:0] |-----------------------------------------------------------------------------------------------TDES2 | Buffer1 Address [31:0] |-----------------------------------------------------------------------------------------------TDES3 | Buffer2 Address [31:0] / Next Descriptor Address [31:0] |-----------------------------------------------------------------------------------------------*//*** @brief Bit definition of TDES0 register: DMA Tx descriptor status register*/#define ETH_DMATXDESC_OWN 0x80000000U /*!< OWN bit: descriptor is owned by DMA engine */#define ETH_DMATXDESC_IC 0x40000000U /*!< Interrupt on Completion */#define ETH_DMATXDESC_LS 0x20000000U /*!< Last Segment */#define ETH_DMATXDESC_FS 0x10000000U /*!< First Segment */#define ETH_DMATXDESC_DC 0x08000000U /*!< Disable CRC */#define ETH_DMATXDESC_DP 0x04000000U /*!< Disable Padding */#define ETH_DMATXDESC_TTSE 0x02000000U /*!< Transmit Time Stamp Enable */#define ETH_DMATXDESC_CIC 0x00C00000U /*!< Checksum Insertion Control: 4 cases */#define ETH_DMATXDESC_CIC_BYPASS 0x00000000U /*!< Do Nothing: Checksum Engine is bypassed */#define ETH_DMATXDESC_CIC_IPV4HEADER 0x00400000U /*!< IPV4 header Checksum Insertion */#define ETH_DMATXDESC_CIC_TCPUDPICMP_SEGMENT 0x00800000U /*!< TCP/UDP/ICMP Checksum Insertion calculated over segment only */#define ETH_DMATXDESC_CIC_TCPUDPICMP_FULL 0x00C00000U /*!< TCP/UDP/ICMP Checksum Insertion fully calculated */#define ETH_DMATXDESC_TER 0x00200000U /*!< Transmit End of Ring */#define ETH_DMATXDESC_TCH 0x00100000U /*!< Second Address Chained */#define ETH_DMATXDESC_TTSS 0x00020000U /*!< Tx Time Stamp Status */#define ETH_DMATXDESC_IHE 0x00010000U /*!< IP Header Error */#define ETH_DMATXDESC_ES 0x00008000U /*!< Error summary: OR of the following bits: UE || ED || EC || LCO || NC || LCA || FF || JT */#define ETH_DMATXDESC_JT 0x00004000U /*!< Jabber Timeout */#define ETH_DMATXDESC_FF 0x00002000U /*!< Frame Flushed: DMA/MTL flushed the frame due to SW flush */#define ETH_DMATXDESC_PCE 0x00001000U /*!< Payload Checksum Error */#define ETH_DMATXDESC_LCA 0x00000800U /*!< Loss of Carrier: carrier lost during transmission */#define ETH_DMATXDESC_NC 0x00000400U /*!< No Carrier: no carrier signal from the transceiver */#define ETH_DMATXDESC_LCO 0x00000200U /*!< Late Collision: transmission aborted due to collision */#define ETH_DMATXDESC_EC 0x00000100U /*!< Excessive Collision: transmission aborted after 16 collisions */#define ETH_DMATXDESC_VF 0x00000080U /*!< VLAN Frame */#define ETH_DMATXDESC_CC 0x00000078U /*!< Collision Count */#define ETH_DMATXDESC_ED 0x00000004U /*!< Excessive Deferral */#define ETH_DMATXDESC_UF 0x00000002U /*!< Underflow Error: late data arrival from the memory */#define ETH_DMATXDESC_DB 0x00000001U /*!< Deferred Bit *//*** @brief Bit definition of TDES1 register*/#define ETH_DMATXDESC_TBS2 0x1FFF0000U /*!< Transmit Buffer2 Size */#define ETH_DMATXDESC_TBS1 0x00001FFFU /*!< Transmit Buffer1 Size *//*** @brief Bit definition of TDES2 register*/#define ETH_DMATXDESC_B1AP 0xFFFFFFFFU /*!< Buffer1 Address Pointer *//*** @brief Bit definition of TDES3 register*/#define ETH_DMATXDESC_B2AP 0xFFFFFFFFU /*!< Buffer2 Address Pointer *//*---------------------------------------------------------------------------------------------TDES6 | Transmit Time Stamp Low [31:0] |-----------------------------------------------------------------------------------------------TDES7 | Transmit Time Stamp High [31:0] |----------------------------------------------------------------------------------------------*//* Bit definition of TDES6 register */#define ETH_DMAPTPTXDESC_TTSL 0xFFFFFFFFU /* Transmit Time Stamp Low *//* Bit definition of TDES7 register */#define ETH_DMAPTPTXDESC_TTSH 0xFFFFFFFFU /* Transmit Time Stamp High *//*** @}*//** @defgroup ETH_DMA_RX_Descriptor ETH DMA RX Descriptor* @{*//*DMA Rx Descriptor--------------------------------------------------------------------------------------------------------------------RDES0 | OWN(31) | Status [30:0] |---------------------------------------------------------------------------------------------------------------------RDES1 | CTRL(31) | Reserved[30:29] | Buffer2 ByteCount[28:16] | CTRL[15:14] | Reserved(13) | Buffer1 ByteCount[12:0] |---------------------------------------------------------------------------------------------------------------------RDES2 | Buffer1 Address [31:0] |---------------------------------------------------------------------------------------------------------------------RDES3 | Buffer2 Address [31:0] / Next Descriptor Address [31:0] |---------------------------------------------------------------------------------------------------------------------*//*** @brief Bit definition of RDES0 register: DMA Rx descriptor status register*/#define ETH_DMARXDESC_OWN 0x80000000U /*!< OWN bit: descriptor is owned by DMA engine */#define ETH_DMARXDESC_AFM 0x40000000U /*!< DA Filter Fail for the rx frame */#define ETH_DMARXDESC_FL 0x3FFF0000U /*!< Receive descriptor frame length */#define ETH_DMARXDESC_ES 0x00008000U /*!< Error summary: OR of the following bits: DE || OE || IPC || LC || RWT || RE || CE */#define ETH_DMARXDESC_DE 0x00004000U /*!< Descriptor error: no more descriptors for receive frame */#define ETH_DMARXDESC_SAF 0x00002000U /*!< SA Filter Fail for the received frame */#define ETH_DMARXDESC_LE 0x00001000U /*!< Frame size not matching with length field */#define ETH_DMARXDESC_OE 0x00000800U /*!< Overflow Error: Frame was damaged due to buffer overflow */#define ETH_DMARXDESC_VLAN 0x00000400U /*!< VLAN Tag: received frame is a VLAN frame */#define ETH_DMARXDESC_FS 0x00000200U /*!< First descriptor of the frame */#define ETH_DMARXDESC_LS 0x00000100U /*!< Last descriptor of the frame */#define ETH_DMARXDESC_IPV4HCE 0x00000080U /*!< IPC Checksum Error: Rx Ipv4 header checksum error */#define ETH_DMARXDESC_LC 0x00000040U /*!< Late collision occurred during reception */#define ETH_DMARXDESC_FT 0x00000020U /*!< Frame type - Ethernet, otherwise 802.3 */#define ETH_DMARXDESC_RWT 0x00000010U /*!< Receive Watchdog Timeout: watchdog timer expired during reception */#define ETH_DMARXDESC_RE 0x00000008U /*!< Receive error: error reported by MII interface */#define ETH_DMARXDESC_DBE 0x00000004U /*!< Dribble bit error: frame contains non int multiple of 8 bits */#define ETH_DMARXDESC_CE 0x00000002U /*!< CRC error */#define ETH_DMARXDESC_MAMPCE 0x00000001U /*!< Rx MAC Address/Payload Checksum Error: Rx MAC address matched/ Rx Payload Checksum Error *//*** @brief Bit definition of RDES1 register*/#define ETH_DMARXDESC_DIC 0x80000000U /*!< Disable Interrupt on Completion */#define ETH_DMARXDESC_RBS2 0x1FFF0000U /*!< Receive Buffer2 Size */#define ETH_DMARXDESC_RER 0x00008000U /*!< Receive End of Ring */#define ETH_DMARXDESC_RCH 0x00004000U /*!< Second Address Chained */#define ETH_DMARXDESC_RBS1 0x00001FFFU /*!< Receive Buffer1 Size */andCode:/* Definition of the Ethernet driver buffers size and count */// 21/7/22 PH increasing both from 2 to 4 makes zero difference to tx speed, and funnily// enough 1 slightly improves it! But only if the low level poll time is 10ms instead of// 20ms; then going to 1 halves the speed. Going to 1+1 yields an extra 3k of RAM.// BUT ETH_RXBUFNB=2 causes multi-second lockups in LWIP due to broadcast packets! 3 fixes// this, so we use 4 to get a margin. Increasing ETH_TXBUFNB from 2 makes no clear diff.#define ETH_RX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for receive */#define ETH_TX_BUF_SIZE ETH_MAX_PACKET_SIZE /* buffer size for transmit */#define ETH_RXBUFNB 4 // was (4U) /* Rx buffers of size ETH_RX_BUF_SIZE */#define ETH_TXBUFNB 2 // was (4U) /* Tx buffers of size ETH_TX_BUF_SIZE */I can't find anything relating to "RBS field of the RDES1: Receive descriptor Word1".One problem is that ST's code does not follow the 32F4 RM bit and register names... but I found thiswhich tends to suggest they are really loading 1524 in there. So it looks OK. They must be loading "something" because the ETH controller would not work at all otherwise.The code in question is this and while I am not 100% sure it does look like this is loading the actual registers... in ST's typical non-obvious indirectly addressed wayCode:/*** @brief Initializes the DMA Rx descriptors in chain mode.* @param heth pointer to a ETH_HandleTypeDef structure that contains* the configuration information for ETHERNET module* @param DMARxDescTab Pointer to the first Rx desc list* @param RxBuff Pointer to the first RxBuffer list* @param RxBuffCount Number of the used Rx desc in the list* @retval HAL status*/HAL_StatusTypeDef HAL_ETH_DMARxDescListInit(ETH_HandleTypeDef *heth, ETH_DMADescTypeDef *DMARxDescTab, uint8_t *RxBuff, uint32_t RxBuffCount){uint32_t i = 0U;ETH_DMADescTypeDef *DMARxDesc;/* Process Locked */__HAL_LOCK(heth);/* Set the ETH peripheral state to BUSY */heth->State = HAL_ETH_STATE_BUSY;/* Set the Ethernet RxDesc pointer with the first one of the DMARxDescTab list */heth->RxDesc = DMARxDescTab;/* Fill each DMARxDesc descriptor with the right values */for(i=0U; i < RxBuffCount; i++){/* Get the pointer on the ith member of the Rx Desc list */DMARxDesc = DMARxDescTab+i;/* Set Own bit of the Rx descriptor Status */DMARxDesc->Status = ETH_DMARXDESC_OWN;/* Set Buffer1 size and Second Address Chained bit */DMARxDesc->ControlBufferSize = ETH_DMARXDESC_RCH | ETH_RX_BUF_SIZE;/* Set Buffer1 address pointer */DMARxDesc->Buffer1Addr = (uint32_t)(&RxBuff[i*ETH_RX_BUF_SIZE]);if((heth->Init).RxMode == ETH_RXINTERRUPT_MODE){/* Enable Ethernet DMA Rx Descriptor interrupt */DMARxDesc->ControlBufferSize &= ~ETH_DMARXDESC_DIC;}/* Initialize the next descriptor with the Next Descriptor Polling Enable */if(i < (RxBuffCount-1U)){/* Set next descriptor address register with next descriptor base address */DMARxDesc->Buffer2NextDescAddr = (uint32_t)(DMARxDescTab+i+1U);}else{/* For last descriptor, set next descriptor address register equal to the first descriptor base address */DMARxDesc->Buffer2NextDescAddr = (uint32_t)(DMARxDescTab);}}/* Set Receive Descriptor List Address Register */(heth->Instance)->DMARDLAR = (uint32_t) DMARxDescTab;/* Set ETH HAL State to Ready */heth->State= HAL_ETH_STATE_READY;/* Process Unlocked */__HAL_UNLOCK(heth);/* Return function status */return HAL_OK;}Whenever I use that "HAL" code I always strip out the crap like __HAL_LOCK(heth); which is completely meaningless.

author: wek
author_rank: Frequent Contributor
timestamp: August 08, 2025, 08:13:43 am
content: Frame (packet) size and DMA buffer size are two independent thing.The latter is given by the descriptors, and is never violated, i.e. no RAM thrashing is possible.So the process is, that MAC starts to receive data into the internal 2kB FIFO, and when a threshold is reached (I am lazy to look up how much and where is it set), it triggers the DMA to sink data from FIFO. DMA takes the first available descriptor and based on its assigned size sinks some data from the FIFO to buffer given by that descriptor, then goes to next descriptor etc. If it runs out of descriptors/buffers, it sets the overflow flag and simply ignores rest of the frame.Independent from this mechanism, the MAC constrains maximum frame size through what in documentation is called "watchdog", which defaults to 2kB.JW