[PATCH] wifi: libertas_tf: Fix slab-out-of-bounds write in if_usb_send_fw_pkt()

Yang Zi 2959243019 at qq.com
Tue Aug 25 02:24:06 PDT 2026


if_usb_send_fw_pkt() copies a firmware block into the driver's
ep_out_buf using the length taken directly from the firmware header
(fwdata->hdr.datalength) without any validation.  That same untrusted
value is also used as the USB transfer length and to advance
cardp->totalbytes.

A crafted or truncated firmware image can set datalength to a huge value,
which makes the memcpy() into fwdata->data write far past the end of the
1574-byte ep_out_buf (KASAN reports a ~3.5 GiB slab-out-of-bounds write)
and, once totalbytes has run away, makes the subsequent header memcpy()
read past the end of the firmware image.

check_fwfile_format() only checks the cumulative length and never bounds
an individual block, and FW_MAX_DATA_BLK_SIZE (600) is never used.

Validate the block length in if_usb_send_fw_pkt(): reject any block whose
datalength exceeds FW_MAX_DATA_BLK_SIZE or the number of bytes remaining
in the firmware image, and return -EINVAL before the memcpy().  Also
enforce the per-block upper bound in check_fwfile_format() so that
invalid images are rejected up front.

Signed-off-by: Yang Zi <2959243019 at qq.com>
---
diff --git a/drivers/net/wireless/marvell/libertas_tf/if_usb.c b/drivers/net/wireless/marvell/libertas_tf/if_usb.c
index b85c6d783bf7..f82860b77da2 100644
--- a/drivers/net/wireless/marvell/libertas_tf/if_usb.c
+++ b/drivers/net/wireless/marvell/libertas_tf/if_usb.c
@@ -268,6 +268,7 @@ static int if_usb_send_fw_pkt(struct if_usb_card *cardp)
 {
     struct fwdata *fwdata = cardp->ep_out_buf;
     u8 *firmware = (u8 *) cardp->fw->data;
+    u32 datalength;
 
     lbtf_deb_enter(LBTF_DEB_FW);
 
@@ -291,17 +292,24 @@ static int if_usb_send_fw_pkt(struct if_usb_card *cardp)
     cardp->fwlastblksent = cardp->totalbytes;
     cardp->totalbytes += sizeof(struct fwheader);
 
-    memcpy(fwdata->data, &firmware[cardp->totalbytes],
-           le32_to_cpu(fwdata->hdr.datalength));
+    datalength = le32_to_cpu(fwdata->hdr.datalength);
+    if (datalength > FW_MAX_DATA_BLK_SIZE ||
+        cardp->totalbytes > cardp->fw->size ||
+        datalength > cardp->fw->size - cardp->totalbytes) {
+        lbtf_deb_usb2(&cardp->udev->dev,
+                  "invalid firmware block length %u\n", datalength);
+        return -EINVAL;
+    }
 
-    lbtf_deb_usb2(&cardp->udev->dev, "Data length = %d\n",
-             le32_to_cpu(fwdata->hdr.datalength));
+    memcpy(fwdata->data, &firmware[cardp->totalbytes], datalength);
+
+    lbtf_deb_usb2(&cardp->udev->dev, "Data length = %u\n", datalength);
 
     fwdata->seqnum = cpu_to_le32(++cardp->fwseqnum);
-    cardp->totalbytes += le32_to_cpu(fwdata->hdr.datalength);
+    cardp->totalbytes += datalength;
 
     usb_tx_block(cardp, cardp->ep_out_buf, sizeof(struct fwdata) +
-             le32_to_cpu(fwdata->hdr.datalength), 0);
+             datalength, 0);
 
     if (fwdata->hdr.dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) {
         lbtf_deb_usb2(&cardp->udev->dev, "There are data to follow\n");
@@ -775,6 +783,10 @@ static int check_fwfile_format(const u8 *data, u32 totlen)
         blksize = le32_to_cpu(fwh->datalength);
         switch (bincmd) {
         case FW_HAS_DATA_TO_RECV:
+            if (blksize > FW_MAX_DATA_BLK_SIZE) {
+                exit = 1;
+                break;
+            }
             offset = sizeof(struct fwheader) + blksize;
             data += offset;
             len += offset;
@@ -782,6 +794,10 @@ static int check_fwfile_format(const u8 *data, u32 totlen)
                 exit = 1;
             break;
         case FW_HAS_LAST_BLOCK:
+            if (blksize > FW_MAX_DATA_BLK_SIZE) {
+                exit = 1;
+                break;
+            }
             exit = 1;
             ret = 0;
             break;




More information about the libertas-dev mailing list