summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@nbd.name>2026-07-27 15:04:34 +0000
committerFelix Fietkau <nbd@nbd.name>2026-08-01 14:49:43 +0000
commitfc7801b7f11d99abdc2fa0e77e2682dede3e7f56 (patch)
treea31864d0ce5fb05a653b532ba285c4ebe70e1f32
parentd8eb7952fa1e35a350037aa351ff3c637285c735 (diff)
wifi: mt76: mt7925: fix infinite loop in UNI event TLV parsing
The event TLV loops accept a zero-length TLV, which advances neither the cursor nor the remaining length, so a malformed event hangs the caller. mt7925_mcu_uni_roc_event() additionally walked past the end of the skb, since it never checked the declared length against the remainder. Replace the five open-coded loops with a shared iterator that rejects lengths below the TLV header and beyond the remaining buffer, and check the per-tag payload sizes before dereferencing them. While here, make the RSSI monitor event read from the current TLV rather than from the start of the list. Link: https://patch.msgid.link/20260727150434.1778520-15-nbd@nbd.name Signed-off-by: Felix Fietkau <nbd@nbd.name>
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/main.c10
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mcu.c38
-rw-r--r--drivers/net/wireless/mediatek/mt76/mt7925/mcu.h19
3 files changed, 46 insertions, 21 deletions
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 2d79a895713c..475a580b348d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -1562,7 +1562,7 @@ void mt7925_scan_work(struct work_struct *work)
while (true) {
struct sk_buff *skb;
struct tlv *tlv;
- int tlv_len;
+ u32 tlv_len;
spin_lock_bh(&phy->dev->mt76.lock);
skb = __skb_dequeue(&phy->scan_event_list);
@@ -1575,7 +1575,7 @@ void mt7925_scan_work(struct work_struct *work)
tlv = (struct tlv *)skb->data;
tlv_len = skb->len;
- while (tlv_len > 0 && le16_to_cpu(tlv->len) <= tlv_len) {
+ mt7925_for_each_tlv(tlv, tlv_len) {
struct mt7925_mcu_scan_chinfo_event *evt;
switch (le16_to_cpu(tlv->tag)) {
@@ -1588,6 +1588,9 @@ void mt7925_scan_work(struct work_struct *work)
}
break;
case UNI_EVENT_SCAN_DONE_CHNLINFO:
+ if (le16_to_cpu(tlv->len) < sizeof(*tlv) + sizeof(*evt))
+ break;
+
evt = (struct mt7925_mcu_scan_chinfo_event *)tlv->data;
mt7925_regd_change(phy, evt->alpha2);
@@ -1599,9 +1602,6 @@ void mt7925_scan_work(struct work_struct *work)
default:
break;
}
-
- tlv_len -= le16_to_cpu(tlv->len);
- tlv = (struct tlv *)((char *)(tlv) + le16_to_cpu(tlv->len));
}
dev_kfree_skb(skb);
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index a6f28aa51a2f..fa29c486a455 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -409,16 +409,18 @@ mt7925_mcu_uni_hif_ctrl_event(struct mt792x_dev *dev, struct sk_buff *skb)
tlv = (struct tlv *)skb->data;
tlv_len = skb->len;
- while (tlv_len > 0 && le16_to_cpu(tlv->len) <= tlv_len) {
+ mt7925_for_each_tlv(tlv, tlv_len) {
switch (le16_to_cpu(tlv->tag)) {
case UNI_EVENT_HIF_CTRL_BASIC:
+ if (le16_to_cpu(tlv->len) <
+ sizeof(struct mt7925_mcu_hif_ctrl_basic_tlv))
+ break;
+
mt7925_mcu_handle_hif_ctrl_basic(dev, tlv);
break;
default:
break;
}
- tlv_len -= le16_to_cpu(tlv->len);
- tlv = (struct tlv *)((char *)(tlv) + le16_to_cpu(tlv->len));
}
}
@@ -426,22 +428,24 @@ static void
mt7925_mcu_uni_roc_event(struct mt792x_dev *dev, struct sk_buff *skb)
{
struct tlv *tlv;
- int i = 0;
+ u32 tlv_len;
skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4);
+ tlv = (struct tlv *)skb->data;
+ tlv_len = skb->len;
- while (i < skb->len) {
- tlv = (struct tlv *)(skb->data + i);
-
+ mt7925_for_each_tlv(tlv, tlv_len) {
switch (le16_to_cpu(tlv->tag)) {
case UNI_EVENT_ROC_GRANT:
+ if (le16_to_cpu(tlv->len) <
+ sizeof(struct mt7925_roc_grant_tlv))
+ break;
+
mt7925_mcu_roc_handle_grant(dev, tlv);
break;
case UNI_EVENT_ROC_GRANT_SUB_LINK:
break;
}
-
- i += le16_to_cpu(tlv->len);
}
}
@@ -477,12 +481,15 @@ mt7925_mcu_tx_done_event(struct mt792x_dev *dev, struct sk_buff *skb)
tlv = (struct tlv *)skb->data;
tlv_len = skb->len;
- while (tlv_len > 0 && le16_to_cpu(tlv->len) <= tlv_len) {
+ mt7925_for_each_tlv(tlv, tlv_len) {
switch (le16_to_cpu(tlv->tag)) {
case UNI_EVENT_TX_DONE_MSG:
if (!is_mt7928(&dev->mt76))
break;
+ if (le16_to_cpu(tlv->len) < sizeof(*evt))
+ break;
+
evt = (struct mt7928_uni_txdone_event *)tlv;
if (evt->status) {
dev_info(dev->mt76.dev,
@@ -509,8 +516,6 @@ mt7925_mcu_tx_done_event(struct mt792x_dev *dev, struct sk_buff *skb)
default:
break;
}
- tlv_len -= le16_to_cpu(tlv->len);
- tlv = (struct tlv *)((char *)(tlv) + le16_to_cpu(tlv->len));
}
}
@@ -547,10 +552,13 @@ mt7925_mcu_rssi_monitor_event(struct mt792x_dev *dev, struct sk_buff *skb)
tlv = (struct tlv *)skb->data;
tlv_len = skb->len;
- while (tlv_len > 0 && le16_to_cpu(tlv->len) <= tlv_len) {
+ mt7925_for_each_tlv(tlv, tlv_len) {
switch (le16_to_cpu(tlv->tag)) {
case UNI_EVENT_RSSI_MONITOR_INFO:
- event = (struct mt7925_uni_rssi_monitor_event *)skb->data;
+ if (le16_to_cpu(tlv->len) < sizeof(*event))
+ break;
+
+ event = (struct mt7925_uni_rssi_monitor_event *)tlv;
ieee80211_iterate_active_interfaces_atomic(dev->mt76.hw,
IEEE80211_IFACE_ITER_RESUME_ALL,
mt7925_mcu_rssi_monitor_iter,
@@ -559,8 +567,6 @@ mt7925_mcu_rssi_monitor_event(struct mt792x_dev *dev, struct sk_buff *skb)
default:
break;
}
- tlv_len -= le16_to_cpu(tlv->len);
- tlv = (struct tlv *)((char *)(tlv) + le16_to_cpu(tlv->len));
}
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h
index 154f792a56bc..11f9eac13ffc 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h
@@ -699,6 +699,25 @@ mt7925_mcu_get_cipher(int cipher)
}
}
+static inline bool
+mt7925_mcu_tlv_valid(struct tlv *tlv, u32 rem)
+{
+ u16 len;
+
+ if (rem < sizeof(*tlv))
+ return false;
+
+ len = le16_to_cpu(tlv->len);
+
+ /* a length below the header size would not advance the cursor */
+ return len >= sizeof(*tlv) && len <= rem;
+}
+
+#define mt7925_for_each_tlv(tlv, rem) \
+ for (; mt7925_mcu_tlv_valid(tlv, rem); \
+ (rem) -= le16_to_cpu((tlv)->len), \
+ (tlv) = (struct tlv *)((u8 *)(tlv) + le16_to_cpu((tlv)->len)))
+
int mt7925_mcu_set_dbdc(struct mt76_phy *phy, bool enable);
int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
struct ieee80211_scan_request *scan_req);