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

Yang Zi <[email protected]>
Newsgroups org.kernel.vger.linux-wireless,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
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 <[email protected]>
---
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;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.