mt7925_load_clc() does not bound the region table against the image size, and its region check "offset + len > fw->size" uses int arithmetic and ignores the region table and trailer. The CLC record loop also uses each record length without a check. A malformed image causes reads past the image, and a record length of zero makes the loop never end. Reject an image that fails mt76_connac2_fw_check(), and reject CLC records that are shorter than the record header or extend past the CLC region, like mt7921_load_clc() does. Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips") Signed-off-by: Felix Fietkau --- .../net/wireless/mediatek/mt76/mt7925/mcu.c | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 2acb02add18f..50f5f57b740e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -911,9 +911,10 @@ static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name) const struct mt7925_clc *clc; struct mt76_dev *mdev = &dev->mt76; struct mt792x_phy *phy = &dev->phy; + size_t clc_len, len, offset = 0; const struct firmware *fw; u8 *clc_base = NULL, hw_encap = 0; - int ret, i, len, offset = 0; + int ret, i; dev->phy.clc_chan_conf = 0xff; dev->regd_user = false; @@ -931,7 +932,7 @@ static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name) if (ret) return ret; - if (!fw || !fw->data || fw->size < sizeof(*hdr)) { + if (mt76_connac2_fw_check(fw, NULL)) { dev_err(mdev->dev, "Invalid firmware\n"); ret = -EINVAL; goto out; @@ -943,13 +944,6 @@ static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name) (hdr->n_region - i) * sizeof(*region)); len = le32_to_cpu(region->len); - /* check if we have valid buffer size */ - if (offset + len > fw->size) { - dev_err(mdev->dev, "Invalid firmware region\n"); - ret = -EINVAL; - goto out; - } - if ((region->feature_set & FW_FEATURE_NON_DL) && region->type == FW_TYPE_CLC) { clc_base = (u8 *)(fw->data + offset); @@ -961,8 +955,20 @@ static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name) if (!clc_base) goto out; - for (offset = 0; offset < len; offset += le32_to_cpu(clc->len)) { + for (offset = 0; offset < len; offset += clc_len) { + if (len - offset < sizeof(*clc)) { + dev_err(mdev->dev, "Invalid CLC record\n"); + ret = -EINVAL; + goto out; + } + clc = (const struct mt7925_clc *)(clc_base + offset); + clc_len = le32_to_cpu(clc->len); + if (clc_len < sizeof(*clc) || clc_len > len - offset) { + dev_err(mdev->dev, "Invalid CLC record\n"); + ret = -EINVAL; + goto out; + } if (clc->idx >= ARRAY_SIZE(phy->clc)) break; @@ -978,7 +984,7 @@ static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name) continue; phy->clc[clc->idx] = devm_kmemdup(mdev->dev, clc, - le32_to_cpu(clc->len), + clc_len, GFP_KERNEL); if (!phy->clc[clc->idx]) { -- 2.47.3