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 <[email protected]>
This commit is contained in:
Felix Fietkau
2026-08-01 17:46:20 +02:00
parent 041c5d4c41
commit a06dbaf0c3
+41 -5
View File
@@ -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;