[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