]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: mt76: mt7996: fix out-of-bounds array access during hardware restart
authorFelix Fietkau <nbd@nbd.name>
Tue, 24 Mar 2026 15:49:03 +0000 (15:49 +0000)
committerFelix Fietkau <nbd@nbd.name>
Tue, 9 Jun 2026 10:15:20 +0000 (10:15 +0000)
During hardware restart, link_id can be IEEE80211_LINK_UNSPECIFIED,
causing an out-of-bounds array access on msta->link[].

Add mt7996_sta_link() and mt7996_sta_link_protected() helper functions
for accessing sta links with proper RCU handling and bounds checking.
Use them for any sta link RCU access.

Reported-by: Chad Monroe <chad@monroe.io>
Link: https://patch.msgid.link/20260324154904.2555603-1-nbd@nbd.name
Signed-off-by: Felix Fietkau <nbd@nbd.name>
drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
drivers/net/wireless/mediatek/mt76/mt7996/mac.c
drivers/net/wireless/mediatek/mt76/mt7996/main.c
drivers/net/wireless/mediatek/mt76/mt7996/mcu.c
drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h

index 34af800964d11d14b74c6eecfb413592204f5822..ef9a9204adf5999e9c5fa22adba86b3f43ecc9d9 100644 (file)
@@ -664,7 +664,7 @@ mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta)
                if (!mlink)
                        continue;
 
-               msta_link = rcu_dereference(msta->link[link_id]);
+               msta_link = mt7996_sta_link(msta, link_id);
                if (!msta_link)
                        continue;
 
@@ -1042,7 +1042,7 @@ static ssize_t mt7996_link_sta_fixed_rate_set(struct file *file,
 
        mutex_lock(&dev->mt76.mutex);
 
-       msta_link = mt76_dereference(msta->link[link_sta->link_id], &dev->mt76);
+       msta_link = mt7996_sta_link_protected(dev, msta, link_sta->link_id);
        if (!msta_link) {
                ret = -EINVAL;
                goto out;
index e2a83da3a09c0a3f961726ab6c2b86b3c6a4d469..c98446057282ae7251914685aa625cf5147c4870 100644 (file)
@@ -48,7 +48,7 @@ static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev,
                if (mlink->band_idx != band_idx)
                        continue;
 
-               msta_link = rcu_dereference(msta->link[i]);
+               msta_link = mt7996_sta_link(msta, i);
                break;
        }
 
@@ -1038,7 +1038,7 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
        if (link_id != wcid->link_id && link_id != IEEE80211_LINK_UNSPECIFIED) {
                if (msta) {
                        struct mt7996_sta_link *msta_link =
-                               rcu_dereference(msta->link[link_id]);
+                               mt7996_sta_link(msta, link_id);
 
                        if (msta_link)
                                wcid = &msta_link->wcid;
@@ -1346,7 +1346,7 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len)
                                         IEEE80211_MLD_MAX_NUM_LINKS) {
                                struct mt7996_sta_link *msta_link;
 
-                               msta_link = rcu_dereference(msta->link[id]);
+                               msta_link = mt7996_sta_link(msta, id);
                                if (!msta_link)
                                        continue;
 
index 9b8db6efb5ac7bae84e5e30a9c41ad905162d263..b3cbf68bb53d66cf4646a5f0d0a39f297be3353d 100644 (file)
@@ -207,8 +207,7 @@ mt7996_set_hw_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
                struct mt7996_sta *msta;
 
                msta = (struct mt7996_sta *)sta->drv_priv;
-               msta_link = mt76_dereference(msta->link[link_id],
-                                            &dev->mt76);
+               msta_link = mt7996_sta_link_protected(dev, msta, link_id);
                if (!msta_link)
                        return 0;
 
@@ -1384,7 +1383,7 @@ mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif,
                if (!link)
                        continue;
 
-               msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+               msta_link = mt7996_sta_link_protected(dev, msta, link_id);
                if (!msta_link)
                        continue;
 
@@ -1576,7 +1575,7 @@ static void mt7996_tx(struct ieee80211_hw *hw,
        if (msta) {
                struct mt7996_sta_link *msta_link;
 
-               msta_link = rcu_dereference(msta->link[link_id]);
+               msta_link = mt7996_sta_link(msta, link_id);
                if (msta_link)
                        wcid = &msta_link->wcid;
        }
@@ -1947,7 +1946,7 @@ static void mt7996_link_sta_rc_update(struct ieee80211_hw *hw,
 
        rcu_read_lock();
 
-       msta_link = rcu_dereference(msta->link[link_sta->link_id]);
+       msta_link = mt7996_sta_link(msta, link_sta->link_id);
        if (msta_link) {
                struct mt7996_dev *dev = mt7996_hw_dev(hw);
 
@@ -1964,7 +1963,7 @@ static void mt7996_sta_rate_ctrl_update(void *data, struct ieee80211_sta *sta)
        struct mt7996_sta_link *msta_link;
        u32 *changed = data;
 
-       msta_link = rcu_dereference(msta->link[msta->deflink_id]);
+       msta_link = mt7996_sta_link(msta, msta->deflink_id);
        if (msta_link)
                mt7996_link_rate_ctrl_update(&changed, msta_link);
 }
@@ -2014,7 +2013,7 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw,
                if (!link)
                        continue;
 
-               msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+               msta_link = mt7996_sta_link_protected(dev, msta, link_id);
                if (!msta_link)
                        continue;
 
@@ -2052,7 +2051,7 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw,
                if (!link)
                        continue;
 
-               msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+               msta_link = mt7996_sta_link_protected(dev, msta, link_id);
                if (!msta_link)
                        continue;
 
@@ -2392,7 +2391,7 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw,
        if (!link)
                return -EIO;
 
-       msta_link = rcu_dereference(msta->link[msta->deflink_id]);
+       msta_link = mt7996_sta_link(msta, msta->deflink_id);
        if (!msta_link)
                return -EIO;
 
index 6aaf3ed9422103c64003b4d5bcfa53768cb1668e..8be40d60ad293f1cd716cc9c9328cb38b6306b2e 100644 (file)
@@ -1136,7 +1136,7 @@ mt7996_mcu_bss_basic_tlv(struct sk_buff *skb,
                                struct mt7996_sta_link *msta_link;
                                int link_id = link_conf->link_id;
 
-                               msta_link = rcu_dereference(msta->link[link_id]);
+                               msta_link = mt7996_sta_link(msta, link_id);
                                if (msta_link)
                                        sta_wlan_idx = msta_link->wcid.idx;
                        }
@@ -1429,7 +1429,7 @@ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev,
                struct mt7996_sta_link *msta_link;
                struct mt7996_vif_link *link;
 
-               msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+               msta_link = mt7996_sta_link_protected(dev, msta, link_id);
                if (!msta_link)
                        continue;
 
@@ -1463,7 +1463,7 @@ int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev,
                struct mt7996_sta_link *msta_link;
                struct mt7996_vif_link *link;
 
-               msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+               msta_link = mt7996_sta_link_protected(dev, msta, link_id);
                if (!msta_link)
                        continue;
 
@@ -2205,7 +2205,7 @@ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct mt7996_sta *msta,
        if (!mlink)
                goto error_unlock;
 
-       msta_link = rcu_dereference(msta->link[link_id]);
+       msta_link = mt7996_sta_link(msta, link_id);
        if (!msta_link)
                goto error_unlock;
 
@@ -2295,7 +2295,7 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct mt7996_sta *msta,
        if (!link)
                goto error_unlock;
 
-       msta_link = rcu_dereference(msta->link[link_id]);
+       msta_link = mt7996_sta_link(msta, link_id);
        if (!msta_link)
                goto error_unlock;
 
@@ -2513,7 +2513,7 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct mt7996_sta *msta,
        if (!link)
                goto error_unlock;
 
-       msta_link = rcu_dereference(msta->link[link_id]);
+       msta_link = mt7996_sta_link(msta, link_id);
        if (!msta_link)
                goto error_unlock;
 
@@ -2668,7 +2668,7 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
        unsigned int link_id;
        struct tlv *tlv;
 
-       msta_link = mt76_dereference(msta->link[msta->deflink_id], &dev->mt76);
+       msta_link = mt7996_sta_link_protected(dev, msta, msta->deflink_id);
        if (!msta_link)
                return;
 
@@ -2682,8 +2682,8 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
        mld_setup->primary_id = cpu_to_le16(msta_link->wcid.idx);
 
        if (nlinks > 1) {
-               msta_link = mt76_dereference(msta->link[msta->seclink_id],
-                                            &dev->mt76);
+               msta_link = mt7996_sta_link_protected(dev, msta,
+                                                     msta->seclink_id);
                if (!msta_link)
                        return;
        }
@@ -2694,7 +2694,7 @@ mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb,
        for_each_sta_active_link(vif, sta, link_sta, link_id) {
                struct mt7996_vif_link *link;
 
-               msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+               msta_link = mt7996_sta_link_protected(dev, msta, link_id);
                if (!msta_link)
                        continue;
 
@@ -2842,7 +2842,7 @@ void mt7996_mcu_update_sta_rec_bw(void *data, struct ieee80211_sta *sta)
        if (!link_sta)
                return;
 
-       msta_link = mt76_dereference(msta->link[link_id], &dev->mt76);
+       msta_link = mt7996_sta_link_protected(dev, msta, link_id);
        if (!msta_link)
                return;
 
index bdcf7245795497655c3e49c72b522dd25db5a251..0dc4198fcf8bbc75d6a2d96aaa052f39723fb9f1 100644 (file)
@@ -642,6 +642,25 @@ mt7996_vif_conf_link(struct mt7996_dev *dev, struct ieee80211_vif *vif,
                                                            link_conf);
 }
 
+static inline struct mt7996_sta_link *
+mt7996_sta_link(struct mt7996_sta *msta, u8 link_id)
+{
+       if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS)
+               return NULL;
+
+       return rcu_dereference(msta->link[link_id]);
+}
+
+static inline struct mt7996_sta_link *
+mt7996_sta_link_protected(struct mt7996_dev *dev, struct mt7996_sta *msta,
+                         u8 link_id)
+{
+       if (link_id >= IEEE80211_MLD_MAX_NUM_LINKS)
+               return NULL;
+
+       return mt76_dereference(msta->link[link_id], &dev->mt76);
+}
+
 #define mt7996_for_each_phy(dev, phy)                                  \
        for (int __i = 0; __i < ARRAY_SIZE((dev)->radio_phy); __i++)    \
                if (((phy) = (dev)->radio_phy[__i]) != NULL)