From: Felix Fietkau Date: Tue, 24 Mar 2026 15:49:03 +0000 (+0000) Subject: wifi: mt76: mt7996: fix out-of-bounds array access during hardware restart X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7ec087fef32a88410488b764b0f5eef68e51175f;p=thirdparty%2Flinux.git wifi: mt76: mt7996: fix out-of-bounds array access during hardware restart 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 Link: https://patch.msgid.link/20260324154904.2555603-1-nbd@nbd.name Signed-off-by: Felix Fietkau --- diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c index 34af800964d1..ef9a9204adf5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c @@ -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; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index e2a83da3a09c..c98446057282 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -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; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 9b8db6efb5ac..b3cbf68bb53d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -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; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index 6aaf3ed94221..8be40d60ad29 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -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; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index bdcf72457954..0dc4198fcf8b 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -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)