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
