diff --git a/mt7996/mcu.c b/mt7996/mcu.c index c8a9f316..e6e2bbcd 100644 --- a/mt7996/mcu.c +++ b/mt7996/mcu.c @@ -845,22 +845,29 @@ mt7996_mcu_rx_ps_sync(struct mt7996_dev *dev, struct sk_buff *skb) struct tlv *tlv; int len; + if (skb->len < sizeof(*event)) + return; + skb_pull(skb, sizeof(*event)); len = skb->len; - while (len > sizeof(*tlv)) { + while (len >= sizeof(*tlv)) { u16 tag, tag_len; tlv = (struct tlv *)skb->data; tag = le16_to_cpu(tlv->tag); tag_len = le16_to_cpu(tlv->len); - if (tag_len > len) + /* a tag_len below the header size would not advance the buffer */ + if (tag_len < sizeof(*tlv) || tag_len > len) break; switch (tag) { case UNI_PS_CLIENT_INFO: { struct mt7996_mcu_ps_client_info *info = (void *)tlv; + if (tag_len < sizeof(*info)) + break; + mt7996_mcu_ps_transition(dev, le16_to_cpu(info->wlan_idx), info->ps_bit); @@ -868,9 +875,14 @@ mt7996_mcu_rx_ps_sync(struct mt7996_dev *dev, struct sk_buff *skb) } case UNI_PS_MULTI_CLIENT_INFO: { struct mt7996_mcu_ps_multi_client_info *info = (void *)tlv; - u16 cnt = le16_to_cpu(info->sta_cnt); + u16 cnt; int i; + if (tag_len < sizeof(*info)) + break; + + cnt = min_t(u16, le16_to_cpu(info->sta_cnt), + (tag_len - sizeof(*info)) / sizeof(info->sta_ps_info[0])); for (i = 0; i < cnt; i++) { u16 entry = le16_to_cpu(info->sta_ps_info[i]);