Linux wireless drivers development
 help / color / mirror / Atom feed
From: Yang Zi <2959243019@qq.com>
To: johannes.berg@intel.com, johan@kernel.org, kees@kernel.org
Cc: linux-wireless@vger.kernel.org, libertas-dev@lists.infradead.org,
	linux-kernel@vger.kernel.org
Subject: [PATCH] wifi: libertas_tf: Fix slab-out-of-bounds write in if_usb_send_fw_pkt()
Date: Tue, 25 Aug 2026 17:24:06 +0800	[thread overview]
Message-ID: <tencent_E7F524BBDD4B458C2FD4C1DBE86C7C062105@qq.com> (raw)

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@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;


             reply	other threads:[~2026-08-25  9:24 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-25  9:24 Yang Zi [this message]
2026-08-25 14:13 ` [PATCH] wifi: libertas_tf: Fix slab-out-of-bounds write in if_usb_send_fw_pkt() Johan Hovold

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=tencent_E7F524BBDD4B458C2FD4C1DBE86C7C062105@qq.com \
    --to=2959243019@qq.com \
    --cc=johan@kernel.org \
    --cc=johannes.berg@intel.com \
    --cc=kees@kernel.org \
    --cc=libertas-dev@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-wireless@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox