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;