From: Ana Bobillo <[email protected]>

lan7x_eth_recv() returns the frame that follows rx command A, B and C,
10 bytes into the transfer, but checks only that the transfer holds
command A and that the frame fits in what follows command A. A transfer
of 4 to 9 bytes, or a frame length up to 6 bytes longer than what
follows the header, hands the network stack a frame that runs past the
received data.

On LAN75xx, command A and B are followed by 2 bytes of padding rather
than command C, and the length in command A counts them, as Linux
smsc75xx_rx_fixup() has it: the frame handed up was 2 bytes longer than
the one received, running past the transfer. Take the padding out of the
length there, so that the frame can be bounded by what follows the
header on both.

lan7x_free_pkt() also steps over only 4 bytes of header, so the tail of
a frame is parsed as the header of another. Step over the 10 bytes of
header, the frame and the padding that aligns the next header to
4 bytes, as Linux lan78xx_rx() and smsc75xx_rx_fixup() do.

Signed-off-by: Ana Bobillo <[email protected]>
---
 drivers/usb/eth/lan7x.c | 31 ++++++++++++++++++++-----------
 drivers/usb/eth/lan7x.h |  4 ++++
 2 files changed, 24 insertions(+), 11 deletions(-)

diff --git a/drivers/usb/eth/lan7x.c b/drivers/usb/eth/lan7x.c
index 0a283619ae5..14c3ace4cc9 100644
--- a/drivers/usb/eth/lan7x.c
+++ b/drivers/usb/eth/lan7x.c
@@ -446,8 +446,8 @@ int lan7x_eth_recv(struct udevice *dev, int flags, uchar 
**packetp)
         * 1st 4 bytes contain the length of the actual data plus error info.
         * Extract data length.
         */
-       if (len < sizeof(packet_len)) {
-               debug("Rx: incomplete packet length\n");
+       if (len < RX_CMD_LEN) {
+               debug("Rx: incomplete packet header\n");
                goto err;
        }
        memcpy(&rx_cmd_a, ptr, sizeof(rx_cmd_a));
@@ -458,18 +458,26 @@ int lan7x_eth_recv(struct udevice *dev, int flags, uchar 
**packetp)
        }
        packet_len = (u16) (rx_cmd_a & RX_CMD_A_LEN_MASK);
 
-       if (packet_len > len - sizeof(packet_len)) {
-               debug("Rx: too large packet: %d\n", packet_len);
-               goto err;
-       }
-
        /*
         * For LAN7x, the length in command A does not
         * include command A, B, and C length.
-        * So use it as is.
+        * On LAN75xx, command A and B are followed by 2 bytes of
+        * padding instead, which the length does include.
         */
+       if (priv->chipid == ID_REV_CHIP_ID_7500) {
+               if (packet_len < RX_LAN75XX_PADDING) {
+                       debug("Rx: too small packet: %d\n", packet_len);
+                       goto err;
+               }
+               packet_len -= RX_LAN75XX_PADDING;
+       }
+
+       if (packet_len > len - RX_CMD_LEN) {
+               debug("Rx: too large packet: %d\n", packet_len);
+               goto err;
+       }
 
-       *packetp = ptr + 10;
+       *packetp = ptr + RX_CMD_LEN;
        return packet_len;
 
 err:
@@ -481,8 +489,9 @@ int lan7x_free_pkt(struct udevice *dev, uchar *packet, int 
packet_len)
 {
        struct lan7x_private *priv = dev_get_priv(dev);
 
-       packet_len = ALIGN(packet_len, 4);
-       usb_ether_advance_rxbuf(&priv->ueth, sizeof(u32) + packet_len);
+       /* Skip command A, B and C, the frame and its padding to 4 bytes */
+       packet_len = ALIGN(RX_CMD_LEN + packet_len, 4);
+       usb_ether_advance_rxbuf(&priv->ueth, packet_len);
 
        return 0;
 }
diff --git a/drivers/usb/eth/lan7x.h b/drivers/usb/eth/lan7x.h
index 9480f4f6d15..29f06c076c5 100644
--- a/drivers/usb/eth/lan7x.h
+++ b/drivers/usb/eth/lan7x.h
@@ -23,6 +23,10 @@
 #define RX_CMD_A_RXE                   BIT(18)
 #define RX_CMD_A_LEN_MASK              0x00003FFF
 
+/* Rx Command A, B and C (or padding) in front of each received frame */
+#define RX_CMD_LEN                     10
+#define RX_LAN75XX_PADDING             2
+
 /* SCSRs */
 #define ID_REV                         0x00
 #define ID_REV_CHIP_ID_MASK            0xFFFF0000

Reply via email to