* [PATCH mt76-next 01/11] wifi: mt76: mt7921: validate CLC event length
@ 2026-10-24 7:19 Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 02/11] wifi: mt76: mt7921: validate NIC capability TLV payload lengths Felix Fietkau
` (9 more replies)
0 siblings, 10 replies; 11+ messages in thread
From: Felix Fietkau @ 2026-10-24 7:19 UTC (permalink / raw)
To: linux-wireless
With CLC_CAP_EVT_EN set, __mt7921_mcu_set_clc() reads chan_conf from the
SET_CLC response without checking the response length. A truncated
response causes an out-of-bounds read.
Reject a response that is too short for the CLC info TLV.
Fixes: 4fc8df50fd41 ("wifi: mt76: mt7921: get regulatory information from the clc event")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
drivers/net/wireless/mediatek/mt76/mt7921/mcu.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
index 89d2c11b97ee..79fead3ccce1 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
@@ -1446,6 +1446,12 @@ int __mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
if (ret_skb) {
struct mt7921_clc_info_tlv *info;
+ if (ret_skb->len < 4 + offsetofend(struct mt7921_clc_info_tlv,
+ chan_conf)) {
+ dev_kfree_skb(ret_skb);
+ return -EINVAL;
+ }
+
info = (struct mt7921_clc_info_tlv *)(ret_skb->data + 4);
dev->phy.clc_chan_conf = info->chan_conf;
dev_kfree_skb(ret_skb);
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH mt76-next 02/11] wifi: mt76: mt7921: validate NIC capability TLV payload lengths
2026-10-24 7:19 [PATCH mt76-next 01/11] wifi: mt76: mt7921: validate CLC event length Felix Fietkau
@ 2026-10-24 7:19 ` Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 03/11] wifi: mt76: mt7925: validate NIC capability TLV lengths Felix Fietkau
` (8 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Felix Fietkau @ 2026-10-24 7:19 UTC (permalink / raw)
To: linux-wireless
mt7921_mcu_get_nic_capability() checks that each TLV fits in the
response, but not that the TLV is long enough for the fields read from
it. A short TLV at the end of the response causes an out-of-bounds read,
for example of the eight-byte chip capability.
Skip a TLV that is too short for the fields its parser reads.
Fixes: f7d2958ca461 ("mt76: connac: add mt76_connac_mcu_get_nic_capability utility routine")
Fixes: 8c94f0e63bb3 ("mt76: connac: extend mcu_get_nic_capability")
Fixes: 4fc8df50fd41 ("wifi: mt76: mt7921: get regulatory information from the clc event")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
.../net/wireless/mediatek/mt76/mt7921/mcu.c | 24 +++++++++++++------
1 file changed, 17 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
index 79fead3ccce1..9b2fa1db7642 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
@@ -546,7 +546,7 @@ static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name)
}
static void mt7921_mcu_parse_tx_resource(struct mt76_dev *dev,
- struct sk_buff *skb)
+ struct sk_buff *skb, int len)
{
struct mt76_sdio *sdio = &dev->sdio;
struct mt7921_tx_resource {
@@ -561,6 +561,9 @@ static void mt7921_mcu_parse_tx_resource(struct mt76_dev *dev,
u8 pad[3];
} __packed * tx_res;
+ if (len < offsetofend(struct mt7921_tx_resource, pp_padding))
+ return;
+
tx_res = (struct mt7921_tx_resource *)skb->data;
sdio->sched.pse_data_quota = le32_to_cpu(tx_res->pse_data_quota);
sdio->pse_mcu_quota_max = le32_to_cpu(tx_res->pse_mcu_quota);
@@ -573,7 +576,7 @@ static void mt7921_mcu_parse_tx_resource(struct mt76_dev *dev,
}
static void mt7921_mcu_parse_phy_cap(struct mt76_dev *dev,
- struct sk_buff *skb)
+ struct sk_buff *skb, int len)
{
struct mt7921_phy_cap {
u8 ht;
@@ -595,6 +598,9 @@ static void mt7921_mcu_parse_phy_cap(struct mt76_dev *dev,
WF0_5G
};
+ if (len < offsetofend(struct mt7921_phy_cap, hw_path))
+ return;
+
cap = (struct mt7921_phy_cap *)skb->data;
dev->phy.antenna_mask = BIT(cap->nss) - 1;
@@ -644,21 +650,25 @@ static int mt7921_mcu_get_nic_capability(struct mt792x_phy *mphy)
switch (le32_to_cpu(tlv->type)) {
case MT_NIC_CAP_6G:
- phy->cap.has_6ghz = skb->data[0];
+ if (len)
+ phy->cap.has_6ghz = skb->data[0];
break;
case MT_NIC_CAP_MAC_ADDR:
- memcpy(phy->macaddr, (void *)skb->data, ETH_ALEN);
+ if (len >= ETH_ALEN)
+ memcpy(phy->macaddr, (void *)skb->data, ETH_ALEN);
break;
case MT_NIC_CAP_PHY:
- mt7921_mcu_parse_phy_cap(phy->dev, skb);
+ mt7921_mcu_parse_phy_cap(phy->dev, skb, len);
break;
case MT_NIC_CAP_TX_RESOURCE:
if (mt76_is_sdio(phy->dev))
mt7921_mcu_parse_tx_resource(phy->dev,
- skb);
+ skb, len);
break;
case MT_NIC_CAP_CHIP_CAP:
- memcpy(&mphy->chip_cap, (void *)skb->data, sizeof(u64));
+ if (len >= sizeof(u64))
+ memcpy(&mphy->chip_cap, (void *)skb->data,
+ sizeof(u64));
break;
default:
break;
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH mt76-next 03/11] wifi: mt76: mt7925: validate NIC capability TLV lengths
2026-10-24 7:19 [PATCH mt76-next 01/11] wifi: mt76: mt7921: validate CLC event length Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 02/11] wifi: mt76: mt7921: validate NIC capability TLV payload lengths Felix Fietkau
@ 2026-10-24 7:19 ` Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 04/11] wifi: mt76: mt7925: free the MCU event when linearising fails Felix Fietkau
` (7 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Felix Fietkau @ 2026-10-24 7:19 UTC (permalink / raw)
To: linux-wireless
mt7925_mcu_get_nic_capability() checks that each TLV fits in the
response, but not that the TLV is long enough for its own header or for
the fields read from it. A short chip capability TLV at the end of the
response causes an out-of-bounds read of its eight-byte value. A TLV
length below the header size makes the loop parse overlapping TLVs.
Stop at a TLV shorter than its header, and skip a TLV that is too short
for the fields its parser reads, like mt7921 does.
Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips")
Fixes: 86c051f2c418 ("wifi: mt76: mt7925: enabling MLO when the firmware supports it")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
.../net/wireless/mediatek/mt76/mt7925/mcu.c | 29 +++++++++++++------
1 file changed, 20 insertions(+), 9 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index f299a3379adb..d26b90ab5e60 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -1058,7 +1058,7 @@ int mt7925_mcu_get_temperature(struct mt792x_phy *phy)
}
static void
-mt7925_mcu_parse_phy_cap(struct mt792x_dev *dev, char *data)
+mt7925_mcu_parse_phy_cap(struct mt792x_dev *dev, char *data, int len)
{
struct mt76_phy *mphy = &dev->mt76.phy;
struct mt76_dev *mdev = mphy->dev;
@@ -1082,6 +1082,9 @@ mt7925_mcu_parse_phy_cap(struct mt792x_dev *dev, char *data)
WF0_5G
};
+ if (len < offsetofend(struct mt7925_mcu_phy_cap, hw_path))
+ return;
+
cap = (struct mt7925_mcu_phy_cap *)data;
mdev->phy.antenna_mask = BIT(cap->nss) - 1;
@@ -1091,7 +1094,7 @@ mt7925_mcu_parse_phy_cap(struct mt792x_dev *dev, char *data)
}
static void
-mt7925_mcu_parse_eml_cap(struct mt792x_dev *dev, char *data)
+mt7925_mcu_parse_eml_cap(struct mt792x_dev *dev, char *data, int len)
{
struct mt7925_mcu_eml_cap {
u8 rsv[4];
@@ -1099,6 +1102,9 @@ mt7925_mcu_parse_eml_cap(struct mt792x_dev *dev, char *data)
u8 rsv2[6];
} __packed * cap;
+ if (len < offsetofend(struct mt7925_mcu_eml_cap, eml_cap))
+ return;
+
cap = (struct mt7925_mcu_eml_cap *)data;
dev->phy.eml_cap = le16_to_cpu(cap->eml_cap);
@@ -1139,30 +1145,35 @@ mt7925_mcu_get_nic_capability(struct mt792x_dev *dev)
for (i = 0; i < le16_to_cpu(hdr->n_element); i++) {
struct tlv *tlv = (struct tlv *)skb->data;
- int len;
+ int len, data_len;
if (skb->len < sizeof(*tlv))
break;
len = le16_to_cpu(tlv->len);
- if (skb->len < len)
+ if (len < sizeof(*tlv) || skb->len < len)
break;
+ data_len = len - sizeof(*tlv);
switch (le16_to_cpu(tlv->tag)) {
case MT_NIC_CAP_6G:
- mphy->cap.has_6ghz = !!tlv->data[0];
+ if (data_len)
+ mphy->cap.has_6ghz = !!tlv->data[0];
break;
case MT_NIC_CAP_MAC_ADDR:
- memcpy(mphy->macaddr, (void *)tlv->data, ETH_ALEN);
+ if (data_len >= ETH_ALEN)
+ memcpy(mphy->macaddr, (void *)tlv->data, ETH_ALEN);
break;
case MT_NIC_CAP_PHY:
- mt7925_mcu_parse_phy_cap(dev, tlv->data);
+ mt7925_mcu_parse_phy_cap(dev, tlv->data, data_len);
break;
case MT_NIC_CAP_CHIP_CAP:
- dev->phy.chip_cap = le64_to_cpu(*(__le64 *)tlv->data);
+ if (data_len >= sizeof(__le64))
+ dev->phy.chip_cap =
+ le64_to_cpu(*(__le64 *)tlv->data);
break;
case MT_NIC_CAP_EML_CAP:
- mt7925_mcu_parse_eml_cap(dev, tlv->data);
+ mt7925_mcu_parse_eml_cap(dev, tlv->data, data_len);
break;
default:
break;
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH mt76-next 04/11] wifi: mt76: mt7925: free the MCU event when linearising fails
2026-10-24 7:19 [PATCH mt76-next 01/11] wifi: mt76: mt7921: validate CLC event length Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 02/11] wifi: mt76: mt7921: validate NIC capability TLV payload lengths Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 03/11] wifi: mt76: mt7925: validate NIC capability TLV lengths Felix Fietkau
@ 2026-10-24 7:19 ` Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 05/11] wifi: mt76: mt7996: validate radar, thermal and WED RRO event lengths Felix Fietkau
` (6 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Felix Fietkau @ 2026-10-24 7:19 UTC (permalink / raw)
To: linux-wireless
mt7925_mcu_rx_event() returns without freeing the skb when
skb_linearize() fails, which leaks the event.
Free it, like mt7921_mcu_rx_event() does.
While here, take the descriptor pointer only after linearising, since
skb_linearize() can reallocate the skb head.
Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for mt7925 chips")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
drivers/net/wireless/mediatek/mt76/mt7925/mcu.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index d26b90ab5e60..2acb02add18f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -740,16 +740,15 @@ mt7925_mcu_uni_rx_unsolicited_event(struct mt792x_dev *dev,
void mt7925_mcu_rx_event(struct mt792x_dev *dev, struct sk_buff *skb)
{
- struct mt7925_mcu_rxd *rxd = (struct mt7925_mcu_rxd *)skb->data;
+ struct mt7925_mcu_rxd *rxd;
- if (skb_linearize(skb))
- return;
-
- if (skb->len < offsetofend(struct mt7925_mcu_rxd, ext_eid)) {
+ if (skb_linearize(skb) ||
+ skb->len < offsetofend(struct mt7925_mcu_rxd, ext_eid)) {
dev_kfree_skb(skb);
return;
}
+ rxd = (struct mt7925_mcu_rxd *)skb->data;
if (rxd->option & MCU_UNI_CMD_UNSOLICITED_EVENT) {
mt7925_mcu_uni_rx_unsolicited_event(dev, skb);
return;
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH mt76-next 05/11] wifi: mt76: mt7996: validate radar, thermal and WED RRO event lengths
2026-10-24 7:19 [PATCH mt76-next 01/11] wifi: mt76: mt7921: validate CLC event length Felix Fietkau
` (2 preceding siblings ...)
2026-10-24 7:19 ` [PATCH mt76-next 04/11] wifi: mt76: mt7925: free the MCU event when linearising fails Felix Fietkau
@ 2026-10-24 7:19 ` Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 06/11] wifi: mt76: connac: validate the RAM firmware region table Felix Fietkau
` (5 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Felix Fietkau @ 2026-10-24 7:19 UTC (permalink / raw)
To: linux-wireless
mt7996_mcu_rx_event() only guarantees the receive descriptor fields
used for dispatch. mt7996_mcu_rx_radar_detected(),
mt7996_mcu_rx_thermal_notify() and mt7996_mcu_wed_rro_event() then read
event fields after the descriptor without checking the event length. A
truncated event causes an out-of-bounds read.
Ignore events too short for the fields the handler reads, like the
other unsolicited event handlers do.
Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices")
Fixes: 21f290884bc1 ("wifi: mt76: connac: add thermal protection support for mt7996")
Fixes: a5d028d66836 ("wifi: mt76: mt7996: add wed rro delete session garbage collector")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
index 9037520183f4..590931207d79 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
@@ -544,6 +544,9 @@ mt7996_mcu_rx_radar_detected(struct mt7996_dev *dev, struct sk_buff *skb)
struct mt76_phy *mphy = &dev->mt76.phy;
struct mt7996_mcu_rdd_report *r;
+ if (skb->len < offsetofend(struct mt7996_mcu_rdd_report, rdd_idx))
+ return;
+
r = (struct mt7996_mcu_rdd_report *)skb->data;
switch (r->rdd_idx) {
@@ -763,6 +766,10 @@ mt7996_mcu_rx_thermal_notify(struct mt7996_dev *dev, struct sk_buff *skb)
struct mt7996_mcu_thermal_notify *n;
struct mt7996_phy *phy;
+ if (skb->len < offsetofend(struct mt7996_mcu_thermal_notify,
+ duty_percent))
+ return;
+
n = (struct mt7996_mcu_thermal_notify *)skb->data;
if (le16_to_cpu(n->tag) != THERMAL_NOTIFY_TAG)
@@ -819,7 +826,7 @@ mt7996_mcu_wed_rro_event(struct mt7996_dev *dev, struct sk_buff *skb)
{
struct mt7996_mcu_wed_rro_event *event = (void *)skb->data;
- if (!mt7996_has_hwrro(dev))
+ if (!mt7996_has_hwrro(dev) || skb->len < sizeof(*event))
return;
skb_pull(skb, sizeof(struct mt7996_mcu_rxd) + 4);
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH mt76-next 06/11] wifi: mt76: connac: validate the RAM firmware region table
2026-10-24 7:19 [PATCH mt76-next 01/11] wifi: mt76: mt7921: validate CLC event length Felix Fietkau
` (3 preceding siblings ...)
2026-10-24 7:19 ` [PATCH mt76-next 05/11] wifi: mt76: mt7996: validate radar, thermal and WED RRO event lengths Felix Fietkau
@ 2026-10-24 7:19 ` Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 07/11] wifi: mt76: mt7996: " Felix Fietkau
` (4 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Felix Fietkau @ 2026-10-24 7:19 UTC (permalink / raw)
To: linux-wireless
mt76_connac2_load_ram() and mt76_connac3_load_phy_ram() only check that
the firmware image holds the trailer. The region table in front of the
trailer and the region lengths come from the image and are not checked
against its size. A malformed image makes the driver read past the
image and send that data to the device.
Add mt76_connac2_fw_check(), which checks that the region table and the
sum of the region lengths fit in the image, and reject an image that
fails it.
Fixes: e57b7901469f ("mt76: add mac80211 driver for MT7915 PCIe-based chipsets")
Fixes: 1c099ab44727 ("mt76: mt7921: add MCU support")
Fixes: 4f8ded4d5f0e ("wifi: mt76: mt7925: add MT7928 FWDL support")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
.../wireless/mediatek/mt76/mt76_connac_mcu.c | 39 +++++++++++++++++--
.../wireless/mediatek/mt76/mt76_connac_mcu.h | 3 ++
2 files changed, 39 insertions(+), 3 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
index 9bdfff24b702..88cdc8083bae 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
@@ -3042,6 +3042,39 @@ s8 mt76_connac_get_rate_power_limit(struct mt76_phy *phy,
}
EXPORT_SYMBOL_GPL(mt76_connac_get_rate_power_limit);
+int mt76_connac2_fw_check(const struct firmware *fw, size_t *data_len)
+{
+ const struct mt76_connac2_fw_region *region;
+ const struct mt76_connac2_fw_trailer *hdr;
+ size_t avail, len = 0;
+ int i;
+
+ if (!fw || !fw->data || fw->size < sizeof(*hdr))
+ return -EINVAL;
+
+ hdr = (const void *)(fw->data + fw->size - sizeof(*hdr));
+ avail = fw->size - sizeof(*hdr);
+ if (hdr->n_region > avail / sizeof(*region))
+ return -EINVAL;
+
+ avail -= hdr->n_region * sizeof(*region);
+ region = (const void *)(fw->data + avail);
+ for (i = 0; i < hdr->n_region; i++) {
+ u32 region_len = le32_to_cpu(region[i].len);
+
+ if (region_len > avail - len)
+ return -EINVAL;
+
+ len += region_len;
+ }
+
+ if (data_len)
+ *data_len = len;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt76_connac2_fw_check);
+
static int
mt76_connac_mcu_send_ram_firmware(struct mt76_dev *dev,
const struct mt76_connac2_fw_trailer *hdr,
@@ -3155,7 +3188,7 @@ int mt76_connac2_load_ram(struct mt76_dev *dev, const char *fw_wm,
if (ret)
return ret;
- if (!fw || !fw->data || fw->size < sizeof(*hdr)) {
+ if (mt76_connac2_fw_check(fw, NULL)) {
dev_err(dev->dev, "Invalid firmware\n");
ret = -EINVAL;
goto out;
@@ -3184,7 +3217,7 @@ int mt76_connac2_load_ram(struct mt76_dev *dev, const char *fw_wm,
if (ret)
return ret;
- if (!fw || !fw->data || fw->size < sizeof(*hdr)) {
+ if (mt76_connac2_fw_check(fw, NULL)) {
dev_err(dev->dev, "Invalid firmware\n");
ret = -EINVAL;
goto out;
@@ -3221,7 +3254,7 @@ int mt76_connac3_load_phy_ram(struct mt76_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(dev->dev, "Invalid PHY firmware\n");
ret = -EINVAL;
goto out;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index 74e37d722710..755c83f95f27 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -6,6 +6,8 @@
#include "mt76_connac.h"
+struct firmware;
+
#define FW_FEATURE_SET_ENCRYPT BIT(0)
#define FW_FEATURE_SET_KEY_IDX GENMASK(2, 1)
#define FW_FEATURE_ENCRY_MODE BIT(4)
@@ -2147,6 +2149,7 @@ int mt76_connac_mcu_del_wtbl_all(struct mt76_dev *dev);
int mt76_connac_mcu_rdd_cmd(struct mt76_dev *dev, int cmd, u8 index,
u8 rx_sel, u8 val);
int mt76_connac_mcu_sta_wed_update(struct mt76_dev *dev, struct sk_buff *skb);
+int mt76_connac2_fw_check(const struct firmware *fw, size_t *data_len);
int mt76_connac2_load_ram(struct mt76_dev *dev, const char *fw_wm,
const char *fw_wa);
int mt76_connac3_load_phy_ram(struct mt76_dev *dev, const char *fw_name);
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH mt76-next 07/11] wifi: mt76: mt7996: validate the RAM firmware region table
2026-10-24 7:19 [PATCH mt76-next 01/11] wifi: mt76: mt7921: validate CLC event length Felix Fietkau
` (4 preceding siblings ...)
2026-10-24 7:19 ` [PATCH mt76-next 06/11] wifi: mt76: connac: validate the RAM firmware region table Felix Fietkau
@ 2026-10-24 7:19 ` Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 08/11] wifi: mt76: mt7925: validate CLC firmware regions and records Felix Fietkau
` (3 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Felix Fietkau @ 2026-10-24 7:19 UTC (permalink / raw)
To: linux-wireless
__mt7996_load_ram() only checks that the firmware image holds the
trailer. mt7996_mcu_send_ram_firmware() then walks the region table and
sends each region without checking either against the image size. A
malformed image makes the driver read past the image and send that data
to the device.
Reject the image with mt76_connac2_fw_check(), like the connac2 RAM
loader. The mt7996 trailer and region layouts match the connac2 ones.
Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
index 590931207d79..d2d4253e2ccd 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
@@ -3576,7 +3576,7 @@ static int __mt7996_load_ram(struct mt7996_dev *dev, const char *fw_type,
if (ret)
return ret;
- if (!fw || !fw->data || fw->size < sizeof(*hdr)) {
+ if (mt76_connac2_fw_check(fw, NULL)) {
dev_err(dev->mt76.dev, "Invalid firmware\n");
ret = -EINVAL;
goto out;
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH mt76-next 08/11] wifi: mt76: mt7925: validate CLC firmware regions and records
2026-10-24 7:19 [PATCH mt76-next 01/11] wifi: mt76: mt7921: validate CLC event length Felix Fietkau
` (5 preceding siblings ...)
2026-10-24 7:19 ` [PATCH mt76-next 07/11] wifi: mt76: mt7996: " Felix Fietkau
@ 2026-10-24 7:19 ` Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 09/11] wifi: mt76: connac: validate the ROM patch section table Felix Fietkau
` (2 subsequent siblings)
9 siblings, 0 replies; 11+ messages in thread
From: Felix Fietkau @ 2026-10-24 7:19 UTC (permalink / raw)
To: linux-wireless
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 <nbd@nbd.name>
---
.../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
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH mt76-next 09/11] wifi: mt76: connac: validate the ROM patch section table
2026-10-24 7:19 [PATCH mt76-next 01/11] wifi: mt76: mt7921: validate CLC event length Felix Fietkau
` (6 preceding siblings ...)
2026-10-24 7:19 ` [PATCH mt76-next 08/11] wifi: mt76: mt7925: validate CLC firmware regions and records Felix Fietkau
@ 2026-10-24 7:19 ` Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 10/11] wifi: mt76: mt7996: " Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 11/11] wifi: mt76: mt792x: use mt76_connac2_fw_check() Felix Fietkau
9 siblings, 0 replies; 11+ messages in thread
From: Felix Fietkau @ 2026-10-24 7:19 UTC (permalink / raw)
To: linux-wireless
mt76_connac2_load_patch() only checks that the patch image holds the
header. The section table after the header, and the offset and length
of each section, come from the image and are not checked against its
size. A malformed image makes the driver read past the image and send
that data to the device.
Add mt76_connac2_patch_check(), which checks that the section table and
every section fit in the image, and reject an image that fails it.
Fixes: e57b7901469f ("mt76: add mac80211 driver for MT7915 PCIe-based chipsets")
Fixes: 1c099ab44727 ("mt76: mt7921: add MCU support")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
.../wireless/mediatek/mt76/mt76_connac_mcu.c | 29 ++++++++++++++++++-
.../wireless/mediatek/mt76/mt76_connac_mcu.h | 1 +
2 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
index 88cdc8083bae..ad53bb60d5cd 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c
@@ -3305,6 +3305,33 @@ static u32 mt76_connac2_get_data_mode(struct mt76_dev *dev, u32 info)
return mode;
}
+int mt76_connac2_patch_check(const struct firmware *fw)
+{
+ const struct mt76_connac2_patch_hdr *hdr;
+ const struct mt76_connac2_patch_sec *sec;
+ u32 i, n_region;
+
+ if (!fw || !fw->data || fw->size < sizeof(*hdr))
+ return -EINVAL;
+
+ hdr = (const void *)fw->data;
+ n_region = be32_to_cpu(hdr->desc.n_region);
+ if (n_region > (fw->size - sizeof(*hdr)) / sizeof(*sec))
+ return -EINVAL;
+
+ sec = (const void *)(fw->data + sizeof(*hdr));
+ for (i = 0; i < n_region; i++) {
+ u32 offs = be32_to_cpu(sec[i].offs);
+ u32 len = be32_to_cpu(sec[i].info.len);
+
+ if (offs > fw->size || len > fw->size - offs)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(mt76_connac2_patch_check);
+
int mt76_connac2_load_patch(struct mt76_dev *dev, const char *fw_name)
{
int i, ret, sem, max_len = mt76_is_sdio(dev) ? 2048 : 4096;
@@ -3326,7 +3353,7 @@ int mt76_connac2_load_patch(struct mt76_dev *dev, const char *fw_name)
if (ret)
goto out;
- if (!fw || !fw->data || fw->size < sizeof(*hdr)) {
+ if (mt76_connac2_patch_check(fw)) {
dev_err(dev->dev, "Invalid firmware\n");
ret = -EINVAL;
goto out;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index 755c83f95f27..88281733a3df 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -2153,6 +2153,7 @@ int mt76_connac2_fw_check(const struct firmware *fw, size_t *data_len);
int mt76_connac2_load_ram(struct mt76_dev *dev, const char *fw_wm,
const char *fw_wa);
int mt76_connac3_load_phy_ram(struct mt76_dev *dev, const char *fw_name);
+int mt76_connac2_patch_check(const struct firmware *fw);
int mt76_connac2_load_patch(struct mt76_dev *dev, const char *fw_name);
int mt76_connac3_load_cb_patch(struct mt76_dev *dev, const char *fw_name);
int mt76_connac2_mcu_fill_message(struct mt76_dev *mdev, struct sk_buff *skb,
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH mt76-next 10/11] wifi: mt76: mt7996: validate the ROM patch section table
2026-10-24 7:19 [PATCH mt76-next 01/11] wifi: mt76: mt7921: validate CLC event length Felix Fietkau
` (7 preceding siblings ...)
2026-10-24 7:19 ` [PATCH mt76-next 09/11] wifi: mt76: connac: validate the ROM patch section table Felix Fietkau
@ 2026-10-24 7:19 ` Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 11/11] wifi: mt76: mt792x: use mt76_connac2_fw_check() Felix Fietkau
9 siblings, 0 replies; 11+ messages in thread
From: Felix Fietkau @ 2026-10-24 7:19 UTC (permalink / raw)
To: linux-wireless
mt7996_load_patch() only checks that the patch image holds the header.
The section table after the header, and the offset and length of each
section, come from the image and are not checked against its size. A
malformed image makes the driver read past the image and send that data
to the device.
Reject the image with mt76_connac2_patch_check(), like the connac2 patch
loader. The mt7996 patch header and section layouts match the connac2
ones.
Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices")
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
drivers/net/wireless/mediatek/mt76/mt7996/mcu.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
index d2d4253e2ccd..e3420ac523ab 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
@@ -3447,7 +3447,7 @@ static int mt7996_load_patch(struct mt7996_dev *dev)
if (ret)
goto out;
- if (!fw || !fw->data || fw->size < sizeof(*hdr)) {
+ if (mt76_connac2_patch_check(fw)) {
dev_err(dev->mt76.dev, "Invalid firmware\n");
ret = -EINVAL;
goto out;
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH mt76-next 11/11] wifi: mt76: mt792x: use mt76_connac2_fw_check()
2026-10-24 7:19 [PATCH mt76-next 01/11] wifi: mt76: mt7921: validate CLC event length Felix Fietkau
` (8 preceding siblings ...)
2026-10-24 7:19 ` [PATCH mt76-next 10/11] wifi: mt76: mt7996: " Felix Fietkau
@ 2026-10-24 7:19 ` Felix Fietkau
9 siblings, 0 replies; 11+ messages in thread
From: Felix Fietkau @ 2026-10-24 7:19 UTC (permalink / raw)
To: linux-wireless
mt792x_get_offload_capability() and mt7921_load_clc() open-code the
checks of the firmware region table that mt76_connac2_fw_check() now
provides. Use the helper instead.
mt7921_load_clc() now checks every region, not only the regions in front
of the CLC region.
Signed-off-by: Felix Fietkau <nbd@nbd.name>
---
.../net/wireless/mediatek/mt76/mt7921/mcu.c | 19 ++-------------
.../net/wireless/mediatek/mt76/mt792x_core.c | 23 +++----------------
2 files changed, 5 insertions(+), 37 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
index 9b2fa1db7642..a0caa77339ee 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
@@ -442,7 +442,7 @@ static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name)
struct mt76_dev *mdev = &dev->mt76;
struct mt792x_phy *phy = &dev->phy;
const struct firmware *fw;
- size_t clc_len, fw_data_len, len, offset = 0;
+ size_t clc_len, len, offset = 0;
int ret, i;
u8 *clc_base = NULL, hw_encap = 0;
@@ -462,33 +462,18 @@ static int mt7921_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;
}
hdr = (const void *)(fw->data + fw->size - sizeof(*hdr));
- if (hdr->n_region > (fw->size - sizeof(*hdr)) / sizeof(*region)) {
- dev_err(mdev->dev, "Invalid firmware region table\n");
- ret = -EINVAL;
- goto out;
- }
- fw_data_len = fw->size - sizeof(*hdr) -
- hdr->n_region * sizeof(*region);
-
for (i = 0; i < hdr->n_region; i++) {
region = (const void *)((const u8 *)hdr -
(hdr->n_region - i) * sizeof(*region));
len = le32_to_cpu(region->len);
- /* check if we have valid buffer size */
- if (len > fw_data_len - offset) {
- 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);
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
index 0eee9c7a2bfd..dbea3c2e626f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c
+++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c
@@ -866,38 +866,21 @@ mt792x_get_offload_capability(struct device *dev, const char *fw_wm)
const u8 *data, *end, *fw_end;
const struct firmware *fw;
u8 offload_caps = 0;
- size_t offset = 0;
- int ret, i;
+ size_t offset;
+ int ret;
ret = request_firmware(&fw, fw_wm, dev);
if (ret)
return ret;
- if (!fw || !fw->data || fw->size < sizeof(*hdr))
+ if (mt76_connac2_fw_check(fw, &offset))
goto invalid;
data = fw->data;
hdr = (const void *)(fw->data + fw->size - sizeof(*hdr));
- if (hdr->n_region > (fw->size - sizeof(*hdr)) /
- sizeof(struct mt76_connac2_fw_region))
- goto invalid;
-
fw_end = (const u8 *)hdr -
hdr->n_region * sizeof(struct mt76_connac2_fw_region);
- for (i = 0; i < hdr->n_region; i++) {
- const struct mt76_connac2_fw_region *region;
- u32 region_len;
-
- region = (const void *)((const u8 *)hdr -
- (hdr->n_region - i) * sizeof(*region));
- region_len = le32_to_cpu(region->len);
- if (region_len > fw_end - fw->data - offset)
- goto invalid;
-
- offset += region_len;
- }
-
if (fw_end - fw->data - offset < 16 + sizeof(*rel_info))
goto invalid;
--
2.47.3
^ permalink raw reply related [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-10-24 7:19 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-24 7:19 [PATCH mt76-next 01/11] wifi: mt76: mt7921: validate CLC event length Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 02/11] wifi: mt76: mt7921: validate NIC capability TLV payload lengths Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 03/11] wifi: mt76: mt7925: validate NIC capability TLV lengths Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 04/11] wifi: mt76: mt7925: free the MCU event when linearising fails Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 05/11] wifi: mt76: mt7996: validate radar, thermal and WED RRO event lengths Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 06/11] wifi: mt76: connac: validate the RAM firmware region table Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 07/11] wifi: mt76: mt7996: " Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 08/11] wifi: mt76: mt7925: validate CLC firmware regions and records Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 09/11] wifi: mt76: connac: validate the ROM patch section table Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 10/11] wifi: mt76: mt7996: " Felix Fietkau
2026-10-24 7:19 ` [PATCH mt76-next 11/11] wifi: mt76: mt792x: use mt76_connac2_fw_check() Felix Fietkau
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox