mirror of
https://github.com/outbackdingo/mt76.git
synced 2026-10-24 13:26:48 +00:00
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/[email protected] Signed-off-by: Felix Fietkau <[email protected]>
This commit is contained in:
+5
-5
@@ -1579,7 +1579,7 @@ void mt7925_scan_work(struct work_struct *work)
|
|||||||
while (true) {
|
while (true) {
|
||||||
struct sk_buff *skb;
|
struct sk_buff *skb;
|
||||||
struct tlv *tlv;
|
struct tlv *tlv;
|
||||||
int tlv_len;
|
u32 tlv_len;
|
||||||
|
|
||||||
spin_lock_bh(&phy->dev->mt76.lock);
|
spin_lock_bh(&phy->dev->mt76.lock);
|
||||||
skb = __skb_dequeue(&phy->scan_event_list);
|
skb = __skb_dequeue(&phy->scan_event_list);
|
||||||
@@ -1592,7 +1592,7 @@ void mt7925_scan_work(struct work_struct *work)
|
|||||||
tlv = (struct tlv *)skb->data;
|
tlv = (struct tlv *)skb->data;
|
||||||
tlv_len = skb->len;
|
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;
|
struct mt7925_mcu_scan_chinfo_event *evt;
|
||||||
|
|
||||||
switch (le16_to_cpu(tlv->tag)) {
|
switch (le16_to_cpu(tlv->tag)) {
|
||||||
@@ -1605,6 +1605,9 @@ void mt7925_scan_work(struct work_struct *work)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case UNI_EVENT_SCAN_DONE_CHNLINFO:
|
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;
|
evt = (struct mt7925_mcu_scan_chinfo_event *)tlv->data;
|
||||||
|
|
||||||
mt7925_regd_change(phy, evt->alpha2);
|
mt7925_regd_change(phy, evt->alpha2);
|
||||||
@@ -1616,9 +1619,6 @@ void mt7925_scan_work(struct work_struct *work)
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
tlv_len -= le16_to_cpu(tlv->len);
|
|
||||||
tlv = (struct tlv *)((char *)(tlv) + le16_to_cpu(tlv->len));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_kfree_skb(skb);
|
dev_kfree_skb(skb);
|
||||||
|
|||||||
+22
-16
@@ -409,16 +409,18 @@ mt7925_mcu_uni_hif_ctrl_event(struct mt792x_dev *dev, struct sk_buff *skb)
|
|||||||
tlv = (struct tlv *)skb->data;
|
tlv = (struct tlv *)skb->data;
|
||||||
tlv_len = skb->len;
|
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)) {
|
switch (le16_to_cpu(tlv->tag)) {
|
||||||
case UNI_EVENT_HIF_CTRL_BASIC:
|
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);
|
mt7925_mcu_handle_hif_ctrl_basic(dev, tlv);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
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)
|
mt7925_mcu_uni_roc_event(struct mt792x_dev *dev, struct sk_buff *skb)
|
||||||
{
|
{
|
||||||
struct tlv *tlv;
|
struct tlv *tlv;
|
||||||
int i = 0;
|
u32 tlv_len;
|
||||||
|
|
||||||
skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4);
|
skb_pull(skb, sizeof(struct mt7925_mcu_rxd) + 4);
|
||||||
|
tlv = (struct tlv *)skb->data;
|
||||||
|
tlv_len = skb->len;
|
||||||
|
|
||||||
while (i < skb->len) {
|
mt7925_for_each_tlv(tlv, tlv_len) {
|
||||||
tlv = (struct tlv *)(skb->data + i);
|
|
||||||
|
|
||||||
switch (le16_to_cpu(tlv->tag)) {
|
switch (le16_to_cpu(tlv->tag)) {
|
||||||
case UNI_EVENT_ROC_GRANT:
|
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);
|
mt7925_mcu_roc_handle_grant(dev, tlv);
|
||||||
break;
|
break;
|
||||||
case UNI_EVENT_ROC_GRANT_SUB_LINK:
|
case UNI_EVENT_ROC_GRANT_SUB_LINK:
|
||||||
break;
|
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 = (struct tlv *)skb->data;
|
||||||
tlv_len = skb->len;
|
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)) {
|
switch (le16_to_cpu(tlv->tag)) {
|
||||||
case UNI_EVENT_TX_DONE_MSG:
|
case UNI_EVENT_TX_DONE_MSG:
|
||||||
if (!is_mt7928(&dev->mt76))
|
if (!is_mt7928(&dev->mt76))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if (le16_to_cpu(tlv->len) < sizeof(*evt))
|
||||||
|
break;
|
||||||
|
|
||||||
evt = (struct mt7928_uni_txdone_event *)tlv;
|
evt = (struct mt7928_uni_txdone_event *)tlv;
|
||||||
if (evt->status) {
|
if (evt->status) {
|
||||||
dev_info(dev->mt76.dev,
|
dev_info(dev->mt76.dev,
|
||||||
@@ -509,8 +516,6 @@ mt7925_mcu_tx_done_event(struct mt792x_dev *dev, struct sk_buff *skb)
|
|||||||
default:
|
default:
|
||||||
break;
|
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 = (struct tlv *)skb->data;
|
||||||
tlv_len = skb->len;
|
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)) {
|
switch (le16_to_cpu(tlv->tag)) {
|
||||||
case UNI_EVENT_RSSI_MONITOR_INFO:
|
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_iterate_active_interfaces_atomic(dev->mt76.hw,
|
||||||
IEEE80211_IFACE_ITER_RESUME_ALL,
|
IEEE80211_IFACE_ITER_RESUME_ALL,
|
||||||
mt7925_mcu_rssi_monitor_iter,
|
mt7925_mcu_rssi_monitor_iter,
|
||||||
@@ -559,8 +567,6 @@ mt7925_mcu_rssi_monitor_event(struct mt792x_dev *dev, struct sk_buff *skb)
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tlv_len -= le16_to_cpu(tlv->len);
|
|
||||||
tlv = (struct tlv *)((char *)(tlv) + le16_to_cpu(tlv->len));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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_set_dbdc(struct mt76_phy *phy, bool enable);
|
||||||
int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
|
int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif,
|
||||||
struct ieee80211_scan_request *scan_req);
|
struct ieee80211_scan_request *scan_req);
|
||||||
|
|||||||
Reference in New Issue
Block a user