From a06dbaf0c3dbdd759f6323803e6fd4c6edac3a77 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Thu, 2 Jul 2026 15:14:11 +0000 Subject: [PATCH] wifi: mt76: mt7996: validate firmware event lengths before parsing Two unsolicited event handlers trusted firmware-provided lengths: mt7996_mcu_rx_all_sta_info_event() looped over res->sta_num without checking it against skb->len, reading the flex array out of bounds; and mt7996_mcu_rx_log_message() could compute a negative or oversized length from tlv->len and feed it to %.*s and the debugfs log path. Bound both against the actual skb length. Fixes: adde3eed4a75 ("wifi: mt76: mt7996: Add mcu commands for getting sta tx statistic") Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices") Signed-off-by: Felix Fietkau --- mt7996/mcu.c | 46 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/mt7996/mcu.c b/mt7996/mcu.c index 804923c0..6a0793fe 100644 --- a/mt7996/mcu.c +++ b/mt7996/mcu.c @@ -572,21 +572,32 @@ mt7996_mcu_rx_log_message(struct mt7996_dev *dev, struct sk_buff *skb) { #define UNI_EVENT_FW_LOG_FORMAT 0 struct mt7996_mcu_rxd *rxd = (struct mt7996_mcu_rxd *)skb->data; - const char *data = (char *)&rxd[1] + 4, *type; - struct tlv *tlv = (struct tlv *)data; + const char *data, *type; + struct tlv *tlv; int len; + if (skb->len < sizeof(*rxd)) + return; + if (!(rxd->option & MCU_UNI_CMD_EVENT)) { - len = skb->len - sizeof(*rxd); data = (char *)&rxd[1]; + len = skb->len - sizeof(*rxd); goto out; } + if (skb->len < sizeof(*rxd) + 4 + sizeof(*tlv)) + return; + + tlv = (struct tlv *)((char *)&rxd[1] + 4); if (le16_to_cpu(tlv->tag) != UNI_EVENT_FW_LOG_FORMAT) return; - data += sizeof(*tlv) + 4; + if (le16_to_cpu(tlv->len) < sizeof(*tlv) + 4) + return; + + data = (char *)tlv + sizeof(*tlv) + 4; len = le16_to_cpu(tlv->len) - sizeof(*tlv) - 4; + len = min_t(int, len, skb->len - (int)(data - (char *)skb->data)); out: switch (rxd->s2d_index) { @@ -648,13 +659,38 @@ static void mt7996_mcu_rx_all_sta_info_event(struct mt7996_dev *dev, struct sk_buff *skb) { struct mt7996_mcu_all_sta_info_event *res; + u32 elem_size; + u16 sta_num; u16 i; + if (skb->len < sizeof(struct mt7996_mcu_rxd)) + return; + skb_pull(skb, sizeof(struct mt7996_mcu_rxd)); res = (struct mt7996_mcu_all_sta_info_event *)skb->data; + if (skb->len < sizeof(*res)) + return; - for (i = 0; i < le16_to_cpu(res->sta_num); i++) { + sta_num = le16_to_cpu(res->sta_num); + switch (le16_to_cpu(res->tag)) { + case UNI_ALL_STA_TXRX_RATE: + elem_size = sizeof(res->rate[0]); + break; + case UNI_ALL_STA_TXRX_ADM_STAT: + elem_size = sizeof(res->adm_stat[0]); + break; + case UNI_ALL_STA_TXRX_MSDU_COUNT: + elem_size = sizeof(res->msdu_cnt[0]); + break; + default: + return; + } + + if (sta_num > (skb->len - sizeof(*res)) / elem_size) + sta_num = (skb->len - sizeof(*res)) / elem_size; + + for (i = 0; i < sta_num; i++) { u8 ac; u16 wlan_idx; struct mt76_wcid *wcid;