This is an automated email from the ASF dual-hosted git repository.
xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 421a4faccae drivers/net/lan9250: don't overflow pktbuf on big RX frames
421a4faccae is described below
commit 421a4faccae3aa50807ea90cfd8655312d66a9f0
Author: SofianElmotiem <[email protected]>
AuthorDate: Mon Nov 2 22:27:24 2026 +0100
drivers/net/lan9250: don't overflow pktbuf on big RX frames
The RX path took the frame length straight from the RX status FIFO and
copied that many bytes into pktbuf. Nothing actually stopped it from
going past the end: the SPI path only had a DEBUGASSERT (gone in normal
builds) and the SQI path had no check at all. With the default 590 byte
packet size, pktbuf is 596 bytes, so a normal full-size Ethernet frame
was enough to scribble over the watchdog and work structs after it.
So now:
- frames bigger than MAX_NETDEV_PKTSIZE + 4 (CRC) get dropped, and their
data is skipped with RX_FFWD in RX_DP_CTRL so the FIFO stays in sync
- frames shorter than an Ethernet header + CRC are still read out but
not passed up, since pktlen - 4 used to wrap d_len around for tiny
lengths
- the remaining DEBUGASSERT now covers both SPI and SQI
- while at it, fixed the existing nxstyle complaints in lan9250.c
(CI checks the whole file), no functional change there
Fixes #21112
Signed-off-by: SofianElmotiem <[email protected]>
---
drivers/net/lan9250.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++----
drivers/net/lan9250.h | 4 +++
2 files changed, 67 insertions(+), 5 deletions(-)
diff --git a/drivers/net/lan9250.c b/drivers/net/lan9250.c
index f68f47926ba..e58c8d239ba 100644
--- a/drivers/net/lan9250.c
+++ b/drivers/net/lan9250.c
@@ -131,6 +131,10 @@
#define LAN9250_SQI_TIMEOUT 20
+/* RX data FIFO fast-forward timeout in second */
+
+#define LAN9250_RXFFWD_TIMEOUT 1
+
/* Packet Memory ************************************************************/
/* Misc. Helper Macros ******************************************************/
@@ -270,6 +274,7 @@ static void lan9250_set_phyreg(FAR struct lan9250_driver_s
*priv,
static void lan9250_recv_buffer(FAR struct lan9250_driver_s *priv,
FAR uint8_t *buffer, size_t buflen);
+static void lan9250_discard_frame(FAR struct lan9250_driver_s *priv);
static inline void lan9250_send_buffer(FAR struct lan9250_driver_s *priv,
FAR const uint8_t *buffer,
size_t buflen);
@@ -881,6 +886,8 @@ static void lan9250_recv_buffer(FAR struct lan9250_driver_s
*priv,
struct qspi_meminfo_s meminfo;
int ret;
+ DEBUGASSERT(buflen <= sizeof(priv->pktbuf));
+
meminfo.cmd = LAN9250_SPI_READ;
meminfo.addr = LAN9250_RXDFR;
meminfo.addrlen = sizeof(uint16_t);
@@ -900,6 +907,29 @@ static void lan9250_recv_buffer(FAR struct
lan9250_driver_s *priv,
lan9250_buffer_dump(LAN9250_RXDFR, buffer, buflen);
}
+/****************************************************************************
+ * Name: lan9250_discard_frame
+ *
+ * Description:
+ * Discard the data of the frame at the head of the RX data FIFO by
+ * fast-forwarding past it. The RX status of the frame must have been
+ * read already, and the frame must be at least 4 DWORDs long.
+ *
+ * Input Parameters:
+ * priv - Reference to the driver state structure
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+static void lan9250_discard_frame(FAR struct lan9250_driver_s *priv)
+{
+ lan9250_set_reg(priv, LAN9250_RDCR, RDCR_RXFFWD);
+ lan9250_wait_ready(priv, LAN9250_RDCR, RDCR_RXFFWD, 0,
+ LAN9250_RXFFWD_TIMEOUT);
+}
+
/****************************************************************************
* Name: lan9250_send_buffer
*
@@ -1424,7 +1454,7 @@ static int lan9250_transmit(FAR struct lan9250_driver_s
*priv)
for (int i = 0; i < status_size; i++)
{
- lan9250_get_reg(priv, LAN9250_TXSFR);
+ lan9250_get_reg(priv, LAN9250_TXSFR);
}
if (free_size > priv->dev.d_len)
@@ -1749,6 +1779,19 @@ static void lan9250_rxdone_isr(FAR struct
lan9250_driver_s *priv)
ninfo("pktlen:%d\n", pktlen);
+ /* The length reported by the hardware includes the 4 byte CRC and
+ * can be up to 16383 bytes. Frames that do not fit in d_buf must
+ * not be copied into it; skip their data in the RX data FIFO.
+ */
+
+ if (pktlen > MAX_NETDEV_PKTSIZE + 4)
+ {
+ nwarn("WARNING: Oversized frame dropped (%u)\n", pktlen);
+ NETDEV_RXERRORS(&priv->dev);
+ lan9250_discard_frame(priv);
+ continue;
+ }
+
if (pktlen)
{
/* Copy the data from the receive buffer to priv->dev.d_buf.
@@ -1758,6 +1801,17 @@ static void lan9250_rxdone_isr(FAR struct
lan9250_driver_s *priv)
lan9250_recv_buffer(priv, priv->dev.d_buf, pktlen);
+ /* Runt frames are read out of the FIFO above, but not passed on,
+ * so that d_len cannot underflow.
+ */
+
+ if (pktlen < ETH_HDRLEN + 4)
+ {
+ nwarn("WARNING: Runt frame dropped (%u)\n", pktlen);
+ NETDEV_RXERRORS(&priv->dev);
+ continue;
+ }
+
/* Save the packet length (without the 4 byte CRC)
* in priv->dev.d_len.
*/
@@ -2373,6 +2427,7 @@ static int lan9250_ioctl(FAR struct net_driver_s *dev,
int cmd,
{
struct mii_ioctl_data_s *req =
(struct mii_ioctl_data_s *)((uintptr_t)arg);
+
req->phy_id = 0;
}
break;
@@ -2381,6 +2436,7 @@ static int lan9250_ioctl(FAR struct net_driver_s *dev,
int cmd,
{
struct mii_ioctl_data_s *req =
(struct mii_ioctl_data_s *)((uintptr_t)arg);
+
req->val_out = lan9250_get_phyreg(priv, req->reg_num);
}
break;
@@ -2389,14 +2445,15 @@ static int lan9250_ioctl(FAR struct net_driver_s *dev,
int cmd,
{
struct mii_ioctl_data_s *req =
(struct mii_ioctl_data_s *)((uintptr_t)arg);
+
lan9250_set_phyreg(priv, req->reg_num, req->val_in);
}
break;
#endif /* CONFIG_NETDEV_PHY_IOCTL */
- default:
- nerr("ERROR: Unrecognized IOCTL command: %d\n", cmd);
- ret = -ENOTTY; /* Special return value for this case */
+ default:
+ nerr("ERROR: Unrecognized IOCTL command: %d\n", cmd);
+ ret = -ENOTTY; /* Special return value for this case */
}
/* Un-lock the SPI bus */
@@ -2511,8 +2568,9 @@ int lan9250_uninitialize(FAR const struct lan9250_lower_s
*lower)
{
FAR struct lan9250_driver_s *priv = &g_lan9250;
FAR struct net_driver_s *dev = &priv->dev;
+ int ret;
- int ret = netdev_unregister(dev); /* No such interface yet */
+ ret = netdev_unregister(dev); /* No such interface yet */
if (ret < 0)
{
nerr("ERROR: netdev_unregister failed: %d\n", ret);
diff --git a/drivers/net/lan9250.h b/drivers/net/lan9250.h
index f937aa5fb5c..56bddb59061 100644
--- a/drivers/net/lan9250.h
+++ b/drivers/net/lan9250.h
@@ -234,6 +234,10 @@
/* System Control and Status Registers Fields *******************************/
+/* RX Datapath Control Register */
+
+#define RDCR_RXFFWD 0x80000000
+
/* RX FIFO Status Register */
#define RXFIR_RXSFUS_M 0x00ff0000