[PATCH 3/7] wifi: mt76: mt7996: validate EEPROM response lengths
From: Jiale Yao
Date: Mon Oct 19 2026 - 22:52:48 EST
The EEPROM query reads fixed event fields and copies EFUSE or external
EEPROM data without checking the firmware response length. A truncated
response or an oversized external data length can cause out-of-bounds
reads.
Validate the fixed event, the requested EFUSE bytes, and both the
external data-length field and its payload before copying data.
Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices")
Fixes: f456e1f56c4c ("wifi: mt76: add external EEPROM support for mt799x chipsets")
Signed-off-by: Jiale Yao <yaojiale02@xxxxxxx>
---
.../net/wireless/mediatek/mt76/mt7996/mcu.c | 30 +++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
index 1f7af6359dd7..eb868c2d5108 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
@@ -4399,6 +4399,7 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_l
};
struct mt7996_mcu_eeprom_access_event *event;
struct sk_buff *skb;
+ size_t data_offset;
int ret, cmd;
u32 addr;
@@ -4424,9 +4425,14 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_l
if (ret)
return ret;
+ data_offset = offsetof(struct mt7996_mcu_eeprom_access_event, eeprom);
+ if (skb->len < data_offset) {
+ ret = -EINVAL;
+ goto out;
+ }
+
event = (struct mt7996_mcu_eeprom_access_event *)skb->data;
if (event->valid) {
- u32 ret_len = le32_to_cpu(event->eeprom.ext_eeprom.data_len);
u32 block = mode == EEPROM_MODE_EXT ? MT7996_EXT_EEPROM_BLOCK_SIZE :
MT7996_EEPROM_BLOCK_SIZE;
@@ -4444,16 +4450,35 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_l
case EEPROM_MODE_EFUSE:
if (!buf_len || buf_len > MT7996_EEPROM_BLOCK_SIZE)
buf_len = MT7996_EEPROM_BLOCK_SIZE;
+ if (skb->len - data_offset < buf_len) {
+ ret = -EINVAL;
+ break;
+ }
memcpy(buf, event->eeprom.efuse, buf_len);
break;
- case EEPROM_MODE_EXT:
+ case EEPROM_MODE_EXT: {
+ u32 ret_len;
+
+ if (skb->len - data_offset < sizeof(__le32)) {
+ ret = -EINVAL;
+ break;
+ }
+
+ ret_len = le32_to_cpu(event->eeprom.ext_eeprom.data_len);
+ data_offset += sizeof(__le32);
+ if (ret_len > skb->len - data_offset) {
+ ret = -EINVAL;
+ break;
+ }
+
if (!buf_len || buf_len > MT7996_EXT_EEPROM_BLOCK_SIZE)
buf_len = MT7996_EXT_EEPROM_BLOCK_SIZE;
memcpy(buf, event->eeprom.ext_eeprom.data,
ret_len < buf_len ? ret_len : buf_len);
break;
+ }
default:
ret = -EINVAL;
break;
@@ -4462,6 +4487,7 @@ int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_l
ret = -EINVAL;
}
+out:
dev_kfree_skb(skb);
return ret;
--
2.34.1