All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RESEND 0/4] usb: eth: Check rx header lengths in r8152, asix88179 and lan7x
@ 2026-10-29  6:06 Ana Bobillo
  2026-10-29  6:06 ` [PATCH RESEND 1/4] usb: eth: r8152: Check the rx descriptor fits in the transfer Ana Bobillo
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Ana Bobillo @ 2026-10-29  6:06 UTC (permalink / raw)
  To: Marek Vasut; +Cc: Tom Rini, Simon Glass, Jonas Karlman, u-boot

This is a resend of the series posted on 23 October, now with the
patches inline: the first posting carried them as attachments, which b4
and Patchwork cannot use. The patches are unchanged and still apply to
main.

Tom asked on another of my threads for CI results with each series:
this one ran unchanged in U-Boot's Azure CI through a CI-only pull
request, together with three of my other series (ext4, EFI capsule and
block/disk), and all 91 jobs passed:
https://github.com/u-boot/u-boot/pull/1064
https://dev.azure.com/u-boot/u-boot/_build/results?buildId=13698

Three of the USB Ethernet host drivers take the lengths in the headers
the adapter sends with each received frame without checking them against
the transfer that carried them:

- r8152 reads the rx descriptor before checking that one is there, and
  its bound on the packet length wraps when fewer than 28 bytes are left.
- asix88179 bounds neither the number of packet headers nor the packet
  lengths, as Linux ax88179_rx_fixup() has done since 2022.
- lan7x checks the frame length against what follows the first 4 bytes
  of its 10-byte header, and lan7x_free_pkt() steps over only 4 bytes.
  On LAN75xx the length also counts 2 bytes of padding, so every frame
  was handed up 2 bytes too long.

An adapter that sends a short or inconsistent transfer then gets the
driver to hand the network stack a packet that runs past the received
data, and for r8152 past the receive buffer.

Patches 1-3 add the checks, one driver each, following what the Linux
drivers check. Patch 4 adds DM tests that bind each driver without a USB
device behind it and feed its recv() and free_pkt() prepared transfers,
and enables the drivers in sandbox_defconfig so they are built and run
there. Without patches 1-3, seven of the eleven tests fail: the short
and oversized transfers, the lan7x step to the next frame and the
LAN75xx length. The well-formed cases pass on both, as does the r8152
descriptor length below the CRC size, which the old code rejected
through its u16 wrap and which guards the change to int. With the
series all eleven pass, and the rest of "ut dm" under test/py gives the
same results with and without it.

I tested this in sandbox only; I have none of these adapters.

Jonas, your series "usb: eth: asix88179: Use a fixed URB buffer size and
misc cleanup" from January bounds the packet header count in its
ax88179_eth_recv() rewrite as well ([5/6]). Patch 2 here keeps to the
checks alone, so either should rebase easily on top of the other.

Ana Bobillo (4):
  usb: eth: r8152: Check the rx descriptor fits in the transfer
  usb: eth: asix88179: Bound the rx packet headers and packets
  usb: eth: lan7x: Account for the whole rx command header
  test: dm: Add tests for the USB Ethernet rx header checks

 configs/sandbox_defconfig   |   5 +
 drivers/usb/eth/asix88179.c |  13 +-
 drivers/usb/eth/lan7x.c     |  31 +--
 drivers/usb/eth/lan7x.h     |   4 +
 drivers/usb/eth/r8152.c     |  13 +-
 test/dm/Makefile            |   1 +
 test/dm/usb_eth.c           | 375 ++++++++++++++++++++++++++++++++++++
 7 files changed, 426 insertions(+), 16 deletions(-)
 create mode 100644 test/dm/usb_eth.c


base-commit: acc808479730e13e74199119eb9b2b9113999244

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH RESEND 1/4] usb: eth: r8152: Check the rx descriptor fits in the transfer
  2026-10-29  6:06 [PATCH RESEND 0/4] usb: eth: Check rx header lengths in r8152, asix88179 and lan7x Ana Bobillo
@ 2026-10-29  6:06 ` Ana Bobillo
  2026-10-29  6:06 ` [PATCH RESEND 2/4] usb: eth: asix88179: Bound the rx packet headers and packets Ana Bobillo
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Ana Bobillo @ 2026-10-29  6:06 UTC (permalink / raw)
  To: Marek Vasut; +Cc: Tom Rini, Simon Glass, Jonas Karlman, u-boot

From: Ana Bobillo <anabobillo3@gmail.com>

r8152_eth_recv() reads the rx descriptor at the start of what is left of
the transfer without checking that a whole descriptor is there, and then
bounds the packet length with

	if (packet_len > len - (sizeof(struct rx_desc) + CRC_SIZE))

which is evaluated in size_t: when fewer than 28 bytes are left it wraps
and lets any packet length through, including one that wrapped in its
u16 because the descriptor gave a length below CRC_SIZE. A short
transfer from the adapter then hands the network stack a packet whose
start and length run past the received data, and past the end of the
2 KiB receive buffer.

Require a whole descriptor and CRC before reading the descriptor, and
work in int so that neither length can wrap.

Signed-off-by: Ana Bobillo <anabobillo3@gmail.com>
---
 drivers/usb/eth/r8152.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/eth/r8152.c b/drivers/usb/eth/r8152.c
index e3f20e08c33..19b3f0bc22a 100644
--- a/drivers/usb/eth/r8152.c
+++ b/drivers/usb/eth/r8152.c
@@ -1471,9 +1471,8 @@ int r8152_eth_recv(struct udevice *dev, int flags, uchar **packetp)
 	struct r8152 *tp = dev_get_priv(dev);
 	struct ueth_data *ueth = &tp->ueth;
 	uint8_t *ptr;
-	int ret, len;
+	int ret, len, packet_len;
 	struct rx_desc *rx_desc;
-	u16 packet_len;
 
 	len = usb_ether_get_rx_bytes(ueth, &ptr);
 	debug("%s: first try, len=%d\n", __func__, len);
@@ -1488,12 +1487,18 @@ int r8152_eth_recv(struct udevice *dev, int flags, uchar **packetp)
 		debug("%s: second try, len=%d\n", __func__, len);
 	}
 
+	if (len < (int)(sizeof(struct rx_desc) + CRC_SIZE)) {
+		debug("Rx: incomplete packet: %d\n", len);
+		goto err;
+	}
+
 	rx_desc = (struct rx_desc *)ptr;
 	packet_len = le32_to_cpu(rx_desc->opts1) & RX_LEN_MASK;
 	packet_len -= CRC_SIZE;
 
-	if (packet_len > len - (sizeof(struct rx_desc) + CRC_SIZE)) {
-		debug("Rx: too large packet: %d\n", packet_len);
+	if (packet_len < 0 ||
+	    packet_len > len - (int)(sizeof(struct rx_desc) + CRC_SIZE)) {
+		debug("Rx: bad packet length: %d\n", packet_len);
 		goto err;
 	}
 

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH RESEND 2/4] usb: eth: asix88179: Bound the rx packet headers and packets
  2026-10-29  6:06 [PATCH RESEND 0/4] usb: eth: Check rx header lengths in r8152, asix88179 and lan7x Ana Bobillo
  2026-10-29  6:06 ` [PATCH RESEND 1/4] usb: eth: r8152: Check the rx descriptor fits in the transfer Ana Bobillo
@ 2026-10-29  6:06 ` Ana Bobillo
  2026-10-29  6:06 ` [PATCH RESEND 3/4] usb: eth: lan7x: Account for the whole rx command header Ana Bobillo
  2026-10-29  6:07 ` [PATCH RESEND 4/4] test: dm: Add tests for the USB Ethernet rx header checks Ana Bobillo
  3 siblings, 0 replies; 5+ messages in thread
From: Ana Bobillo @ 2026-10-29  6:06 UTC (permalink / raw)
  To: Marek Vasut; +Cc: Tom Rini, Simon Glass, Jonas Karlman, u-boot

From: Ana Bobillo <anabobillo3@gmail.com>

ax88179_eth_recv() takes the packet count and the offset of the packet
headers from the rx header at the end of the transfer, and checks only
that the offset lies inside the transfer. The count is not bounded, so
the driver walks packet headers past the rx header and the received
data, and no packet length is bounded either, so a packet, and the
start of the next one, can run past the received data.

Check what Linux ax88179_rx_fixup() checks since commit 57bc3d3ae8c1
("net: usb: ax88179_178a: Fix out-of-bounds accesses in RX fixup"): the
packet headers must fit between their offset and the rx header, and each
packet with its padding in front of the packet headers. Keep the end of
the packet data in asix_private for that, and drop the rest of the
transfer when a packet does not fit.

Signed-off-by: Ana Bobillo <anabobillo3@gmail.com>
---
 drivers/usb/eth/asix88179.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/eth/asix88179.c b/drivers/usb/eth/asix88179.c
index 69d3073b669..4c6c9b2796e 100644
--- a/drivers/usb/eth/asix88179.c
+++ b/drivers/usb/eth/asix88179.c
@@ -204,6 +204,7 @@ struct asix_private {
 	struct ueth_data ueth;
 	unsigned pkt_cnt;
 	uint8_t *pkt_data;
+	uint8_t *pkt_end;
 	uint32_t *pkt_hdr;
 	int flags;
 	int rx_urb_size;
@@ -543,6 +544,7 @@ void ax88179_eth_stop(struct udevice *dev)
 	usb_ether_advance_rxbuf(ueth, -1);
 	priv->pkt_cnt = 0;
 	priv->pkt_data = NULL;
+	priv->pkt_end = NULL;
 	priv->pkt_hdr = NULL;
 }
 
@@ -595,14 +597,16 @@ int ax88179_eth_recv(struct udevice *dev, int flags, uchar **packetp)
 			return 0;
 		}
 
+		/* The packet headers must lie in front of rx_hdr */
 		hdr_off = (u16)(rx_hdr >> 16);
-		if (hdr_off > len - 4) {
+		if (hdr_off + 4 * pkt_cnt > len - 4) {
 			usb_ether_advance_rxbuf(ueth, -1);
 			return -EIO;
 		}
 
 		priv->pkt_cnt = pkt_cnt;
 		priv->pkt_data = ptr;
+		priv->pkt_end = ptr + hdr_off;
 		priv->pkt_hdr = (u32 *)(ptr + hdr_off);
 		debug("%s: %d packets received, pkt header at %d\n",
 		      __func__, (int)priv->pkt_cnt, (int)hdr_off);
@@ -611,6 +615,13 @@ int ax88179_eth_recv(struct udevice *dev, int flags, uchar **packetp)
 	le32_to_cpus(priv->pkt_hdr);
 	pkt_len = (*priv->pkt_hdr >> 16) & 0x1fff;
 
+	/* The packet and its padding must lie in front of the packet headers */
+	if (((pkt_len + 7) & 0xFFF8) > priv->pkt_end - priv->pkt_data) {
+		priv->pkt_cnt = 0;
+		usb_ether_advance_rxbuf(ueth, -1);
+		return -EIO;
+	}
+
 	*packetp = priv->pkt_data + 2;
 
 	priv->pkt_data += (pkt_len + 7) & 0xFFF8;

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH RESEND 3/4] usb: eth: lan7x: Account for the whole rx command header
  2026-10-29  6:06 [PATCH RESEND 0/4] usb: eth: Check rx header lengths in r8152, asix88179 and lan7x Ana Bobillo
  2026-10-29  6:06 ` [PATCH RESEND 1/4] usb: eth: r8152: Check the rx descriptor fits in the transfer Ana Bobillo
  2026-10-29  6:06 ` [PATCH RESEND 2/4] usb: eth: asix88179: Bound the rx packet headers and packets Ana Bobillo
@ 2026-10-29  6:06 ` Ana Bobillo
  2026-10-29  6:07 ` [PATCH RESEND 4/4] test: dm: Add tests for the USB Ethernet rx header checks Ana Bobillo
  3 siblings, 0 replies; 5+ messages in thread
From: Ana Bobillo @ 2026-10-29  6:06 UTC (permalink / raw)
  To: Marek Vasut; +Cc: Tom Rini, Simon Glass, Jonas Karlman, u-boot

From: Ana Bobillo <anabobillo3@gmail.com>

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 <anabobillo3@gmail.com>
---
 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

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH RESEND 4/4] test: dm: Add tests for the USB Ethernet rx header checks
  2026-10-29  6:06 [PATCH RESEND 0/4] usb: eth: Check rx header lengths in r8152, asix88179 and lan7x Ana Bobillo
                   ` (2 preceding siblings ...)
  2026-10-29  6:06 ` [PATCH RESEND 3/4] usb: eth: lan7x: Account for the whole rx command header Ana Bobillo
@ 2026-10-29  6:07 ` Ana Bobillo
  3 siblings, 0 replies; 5+ messages in thread
From: Ana Bobillo @ 2026-10-29  6:07 UTC (permalink / raw)
  To: Marek Vasut; +Cc: Tom Rini, Simon Glass, Jonas Karlman, u-boot

From: Ana Bobillo <anabobillo3@gmail.com>

Add DM tests that bind the r8152, ax88179, lan75xx and lan78xx drivers
without probing them and hand their recv() and free_pkt() prepared
transfers in the receive buffer: well-formed ones, which must come back
as the packets they hold, and ones whose header or lengths run past the
transfer, which must be dropped.

Enable CONFIG_USB_HOST_ETHER and these drivers in sandbox_defconfig so
that they are built and tested under sandbox.

Signed-off-by: Ana Bobillo <anabobillo3@gmail.com>
---
 configs/sandbox_defconfig |   5 +
 test/dm/Makefile          |   1 +
 test/dm/usb_eth.c         | 375 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 381 insertions(+)
 create mode 100644 test/dm/usb_eth.c

diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index a3fa5246082..1d409891580 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -359,6 +359,11 @@ CONFIG_USB=y
 CONFIG_DM_USB_GADGET=y
 CONFIG_USB_EMUL=y
 CONFIG_USB_KEYBOARD=y
+CONFIG_USB_HOST_ETHER=y
+CONFIG_USB_ETHER_ASIX88179=y
+CONFIG_USB_ETHER_LAN75XX=y
+CONFIG_USB_ETHER_LAN78XX=y
+CONFIG_USB_ETHER_RTL8152=y
 CONFIG_USB_GADGET=y
 CONFIG_USB_GADGET_DOWNLOAD=y
 CONFIG_USB_ETHER=y
diff --git a/test/dm/Makefile b/test/dm/Makefile
index 1b3a20aa932..e22cbf2c2ba 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -150,6 +150,7 @@ obj-$(CONFIG_TEE) += tee.o
 obj-$(CONFIG_TIMER) += timer.o
 obj-$(CONFIG_TPM_V2) += tpm.o
 obj-$(CONFIG_DM_USB) += usb.o
+obj-$(CONFIG_USB_HOST_ETHER) += usb_eth.o
 obj-$(CONFIG_VIDEO_SANDBOX_SDL) += video.o
 ifeq ($(CONFIG_VIRTIO_SANDBOX),y)
 obj-y += virtio.o
diff --git a/test/dm/usb_eth.c b/test/dm/usb_eth.c
new file mode 100644
index 00000000000..2f8903b71b6
--- /dev/null
+++ b/test/dm/usb_eth.c
@@ -0,0 +1,375 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for the receive header checks of the USB Ethernet drivers
+ *
+ * Each driver is bound without being probed, so that no USB device sits
+ * behind it, and its recv() is handed a prepared transfer in the receive
+ * buffer.
+ */
+
+#include <dm.h>
+#include <net.h>
+#include <usb_ether.h>
+#include <asm/unaligned.h>
+#include <dm/device-internal.h>
+#include <dm/root.h>
+#include <dm/test.h>
+#include <test/ut.h>
+#include "../../drivers/usb/eth/lan7x.h"
+#include "../../drivers/usb/eth/r8152.h"
+
+static u8 rxbuf[256] __maybe_unused;
+
+/*
+ * Bind @drv to a new device and present the first @len bytes of rxbuf to it
+ * as a received transfer. device_of_to_plat() allocates the private data
+ * without probing the device; @ueth_offset is where the driver keeps its
+ * struct ueth_data in there.
+ */
+static int __maybe_unused usb_eth_test_bind(struct unit_test_state *uts,
+					    struct driver *drv,
+					    size_t ueth_offset, int len,
+					    struct udevice **devp,
+					    struct ueth_data **uethp)
+{
+	struct ueth_data *ueth;
+
+	ut_assertok(device_bind(dm_root(), drv, drv->name, NULL, ofnode_null(),
+				devp));
+	ut_assertok(device_of_to_plat(*devp));
+	ueth = dev_get_priv(*devp) + ueth_offset;
+	ueth->rxbuf = rxbuf;
+	ueth->rxsize = sizeof(rxbuf);
+	ueth->rxlen = len;
+	ueth->rxptr = 0;
+	*uethp = ueth;
+
+	return 0;
+}
+
+static int __maybe_unused usb_eth_test_recv(struct udevice *dev,
+					    uchar **packetp)
+{
+	return eth_get_ops(dev)->recv(dev, 0, packetp);
+}
+
+static int __maybe_unused usb_eth_test_free_pkt(struct udevice *dev,
+						uchar *packet, int len)
+{
+	return eth_get_ops(dev)->free_pkt(dev, packet, len);
+}
+
+#if IS_ENABLED(CONFIG_USB_ETHER_RTL8152)
+static int r8152_test_bind(struct unit_test_state *uts, int len,
+			   struct udevice **devp, struct ueth_data **uethp)
+{
+	return usb_eth_test_bind(uts, DM_DRIVER_GET(r8152_eth),
+				 offsetof(struct r8152, ueth), len, devp,
+				 uethp);
+}
+
+/* r8152_eth_recv() returns each packet of an aggregated transfer */
+static int dm_test_r8152_eth_recv(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+
+	/* A packet of 60 bytes, then one of 42 at the next 8-byte boundary */
+	memset(rxbuf, 0, sizeof(rxbuf));
+	put_unaligned_le32(60 + CRC_SIZE, rxbuf);
+	put_unaligned_le32(42 + CRC_SIZE, rxbuf + 88);
+	ut_assertok(r8152_test_bind(uts, 88 + sizeof(struct rx_desc) + 42 +
+				    CRC_SIZE, &dev, &ueth));
+
+	ut_asserteq(60, usb_eth_test_recv(dev, &packet));
+	ut_asserteq_ptr(rxbuf + sizeof(struct rx_desc), packet);
+	ut_assertok(usb_eth_test_free_pkt(dev, packet, 60));
+	ut_asserteq(42, usb_eth_test_recv(dev, &packet));
+	ut_asserteq_ptr(rxbuf + 88 + sizeof(struct rx_desc), packet);
+	ut_assertok(usb_eth_test_free_pkt(dev, packet, 42));
+	ut_asserteq(-EAGAIN, usb_eth_test_recv(dev, &packet));
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_r8152_eth_recv, 0);
+
+/* r8152_eth_recv() rejects a transfer too short for the rx descriptor */
+static int dm_test_r8152_eth_recv_short(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+
+	memset(rxbuf, 0, sizeof(rxbuf));
+	put_unaligned_le32(CRC_SIZE + 2, rxbuf);
+	ut_assertok(r8152_test_bind(uts, 8, &dev, &ueth));
+
+	ut_asserteq(-ENOSPC, usb_eth_test_recv(dev, &packet));
+	ut_asserteq(0, ueth->rxlen);
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_r8152_eth_recv_short, 0);
+
+/* r8152_eth_recv() rejects a descriptor length shorter than the CRC */
+static int dm_test_r8152_eth_recv_crc(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+
+	memset(rxbuf, 0, sizeof(rxbuf));
+	put_unaligned_le32(CRC_SIZE - 2, rxbuf);
+	ut_assertok(r8152_test_bind(uts, sizeof(struct rx_desc) + 64, &dev,
+				    &ueth));
+
+	ut_asserteq(-ENOSPC, usb_eth_test_recv(dev, &packet));
+	ut_asserteq(0, ueth->rxlen);
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_r8152_eth_recv_crc, 0);
+#endif
+
+#if IS_ENABLED(CONFIG_USB_ETHER_ASIX88179)
+/*
+ * struct asix_private is local to the driver; its struct ueth_data comes
+ * first
+ */
+static int ax88179_test_bind(struct unit_test_state *uts, int len,
+			     struct udevice **devp, struct ueth_data **uethp)
+{
+	return usb_eth_test_bind(uts, DM_DRIVER_GET(ax88179_eth), 0, len, devp,
+				 uethp);
+}
+
+/*
+ * Prepare a transfer of 64 bytes of packet data, two packet headers, the
+ * first giving @pkt_len, and the rx header giving @pkt_cnt of them
+ */
+static int ax88179_test_rx(int pkt_cnt, int pkt_len)
+{
+	const int hdr_off = 64;
+
+	memset(rxbuf, 0, sizeof(rxbuf));
+	put_unaligned_le32(pkt_len << 16, rxbuf + hdr_off);
+	put_unaligned_le32(hdr_off << 16 | pkt_cnt, rxbuf + hdr_off + 8);
+
+	return hdr_off + 12;
+}
+
+/* ax88179_eth_recv() returns each packet of the transfer */
+static int dm_test_ax88179_eth_recv(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+	int len;
+
+	/* A packet of 22 bytes padded to 24, then one of 40 up to the headers */
+	len = ax88179_test_rx(2, 22);
+	put_unaligned_le32(40 << 16, rxbuf + 68);
+	ut_assertok(ax88179_test_bind(uts, len, &dev, &ueth));
+
+	ut_asserteq(22, usb_eth_test_recv(dev, &packet));
+	ut_asserteq_ptr(rxbuf + 2, packet);
+	ut_assertok(usb_eth_test_free_pkt(dev, packet, 22));
+	ut_asserteq(40, usb_eth_test_recv(dev, &packet));
+	ut_asserteq_ptr(rxbuf + 24 + 2, packet);
+	ut_assertok(usb_eth_test_free_pkt(dev, packet, 40));
+	ut_asserteq(0, ueth->rxlen);
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_ax88179_eth_recv, 0);
+
+/* ax88179_eth_recv() rejects packet headers running past the rx header */
+static int dm_test_ax88179_eth_recv_pkt_cnt(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+	int len;
+
+	len = ax88179_test_rx(3, 22);
+	ut_assertok(ax88179_test_bind(uts, len, &dev, &ueth));
+
+	ut_asserteq(-EIO, usb_eth_test_recv(dev, &packet));
+	ut_asserteq(0, ueth->rxlen);
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_ax88179_eth_recv_pkt_cnt, 0);
+
+/* ax88179_eth_recv() rejects a packet running past the packet headers */
+static int dm_test_ax88179_eth_recv_pkt_len(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+	int len;
+
+	len = ax88179_test_rx(1, 65);
+	ut_assertok(ax88179_test_bind(uts, len, &dev, &ueth));
+
+	ut_asserteq(-EIO, usb_eth_test_recv(dev, &packet));
+	ut_asserteq(0, ueth->rxlen);
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_ax88179_eth_recv_pkt_len, 0);
+#endif
+
+#if IS_ENABLED(CONFIG_USB_ETHER_LAN75XX) || IS_ENABLED(CONFIG_USB_ETHER_LAN78XX)
+static int lan7x_test_bind(struct unit_test_state *uts, struct driver *drv,
+			   u32 chipid, int len, struct udevice **devp,
+			   struct ueth_data **uethp)
+{
+	struct lan7x_private *priv;
+
+	ut_assertok(usb_eth_test_bind(uts, drv,
+				      offsetof(struct lan7x_private, ueth), len,
+				      devp, uethp));
+	priv = dev_get_priv(*devp);
+	priv->chipid = chipid;
+
+	return 0;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_USB_ETHER_LAN78XX)
+static int lan78xx_test_bind(struct unit_test_state *uts, int len,
+			     struct udevice **devp, struct ueth_data **uethp)
+{
+	return lan7x_test_bind(uts, DM_DRIVER_GET(lan78xx_eth),
+			       ID_REV_CHIP_ID_7800, len, devp, uethp);
+}
+
+/* lan7x_eth_recv() returns the frame behind command A, B and C */
+static int dm_test_lan7x_eth_recv(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+
+	memset(rxbuf, 0, sizeof(rxbuf));
+	put_unaligned_le32(60, rxbuf);
+	ut_assertok(lan78xx_test_bind(uts, 10 + 60, &dev, &ueth));
+
+	ut_asserteq(60, usb_eth_test_recv(dev, &packet));
+	ut_asserteq_ptr(rxbuf + 10, packet);
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_lan7x_eth_recv, 0);
+
+/* lan7x_eth_recv() rejects a transfer too short for command A, B and C */
+static int dm_test_lan7x_eth_recv_short(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+
+	memset(rxbuf, 0, sizeof(rxbuf));
+	put_unaligned_le32(2, rxbuf);
+	ut_assertok(lan78xx_test_bind(uts, 6, &dev, &ueth));
+
+	ut_asserteq(-EINVAL, usb_eth_test_recv(dev, &packet));
+	ut_asserteq(0, ueth->rxlen);
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_lan7x_eth_recv_short, 0);
+
+/* lan7x_eth_recv() rejects a frame running past the transfer */
+static int dm_test_lan7x_eth_recv_len(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+
+	memset(rxbuf, 0, sizeof(rxbuf));
+	put_unaligned_le32(64, rxbuf);
+	ut_assertok(lan78xx_test_bind(uts, 4 + 64, &dev, &ueth));
+
+	ut_asserteq(-EINVAL, usb_eth_test_recv(dev, &packet));
+	ut_asserteq(0, ueth->rxlen);
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_lan7x_eth_recv_len, 0);
+
+/* lan7x_free_pkt() steps over the header, the frame and its padding */
+static int dm_test_lan7x_free_pkt(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+
+	/* A frame of 61 bytes, then one of 60 at the next 4-byte boundary */
+	memset(rxbuf, 0, sizeof(rxbuf));
+	put_unaligned_le32(61, rxbuf);
+	put_unaligned_le32(60, rxbuf + 72);
+	ut_assertok(lan78xx_test_bind(uts, 72 + 10 + 60, &dev, &ueth));
+
+	ut_asserteq(61, usb_eth_test_recv(dev, &packet));
+	ut_assertok(usb_eth_test_free_pkt(dev, packet, 61));
+	ut_asserteq(60, usb_eth_test_recv(dev, &packet));
+	ut_asserteq_ptr(rxbuf + 72 + 10, packet);
+	ut_assertok(usb_eth_test_free_pkt(dev, packet, 60));
+	ut_asserteq(0, ueth->rxlen);
+	ut_asserteq(-EAGAIN, usb_eth_test_recv(dev, &packet));
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_lan7x_free_pkt, 0);
+#endif
+
+#if IS_ENABLED(CONFIG_USB_ETHER_LAN75XX)
+/*
+ * lan7x_eth_recv() returns the LAN75xx frame behind command A, B and the
+ * padding, which its length counts
+ */
+static int dm_test_lan7x_eth_recv_lan75xx(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+
+	memset(rxbuf, 0, sizeof(rxbuf));
+	put_unaligned_le32(2 + 60, rxbuf);
+	ut_assertok(lan7x_test_bind(uts, DM_DRIVER_GET(lan75xx_eth),
+				    ID_REV_CHIP_ID_7500, 10 + 60, &dev, &ueth));
+
+	ut_asserteq(60, usb_eth_test_recv(dev, &packet));
+	ut_asserteq_ptr(rxbuf + 10, packet);
+	ut_assertok(usb_eth_test_free_pkt(dev, packet, 60));
+	ut_asserteq(0, ueth->rxlen);
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_lan7x_eth_recv_lan75xx, 0);
+#endif

^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-10-29  6:07 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-29  6:06 [PATCH RESEND 0/4] usb: eth: Check rx header lengths in r8152, asix88179 and lan7x Ana Bobillo
2026-10-29  6:06 ` [PATCH RESEND 1/4] usb: eth: r8152: Check the rx descriptor fits in the transfer Ana Bobillo
2026-10-29  6:06 ` [PATCH RESEND 2/4] usb: eth: asix88179: Bound the rx packet headers and packets Ana Bobillo
2026-10-29  6:06 ` [PATCH RESEND 3/4] usb: eth: lan7x: Account for the whole rx command header Ana Bobillo
2026-10-29  6:07 ` [PATCH RESEND 4/4] test: dm: Add tests for the USB Ethernet rx header checks Ana Bobillo

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.