* [PATCH 1/2] wifi: mt76: mt7996: unlink TWT flow if MCU rejects agreement
2026-10-18 10:47 [PATCH 0/2] wifi: mt76: mt7996: fix TWT and MLO teardown races Jiale Yao
@ 2026-10-18 10:47 ` Jiale Yao
2026-10-18 10:47 ` [PATCH 2/2] wifi: mt76: mt7996: clear all link WCIDs before RCU removal Jiale Yao
1 sibling, 0 replies; 3+ messages in thread
From: Jiale Yao @ 2026-10-18 10:47 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno,
MeiChia Chiu, Money Wang, Peter Chiu, linux-wireless,
linux-kernel, linux-arm-kernel, linux-mediatek
Cc: Jiale Yao
The flow is added to dev->twt_list before the agreement is sent to the
firmware. If the firmware rejects the agreement, the flow remains linked
while its flow ID is left available.
A later setup can reuse and reinitialize the linked flow, corrupting the
TWT list. Station removal can also leave the stale entry behind.
Remove the flow from the list when adding the firmware agreement fails.
Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices")
Signed-off-by: Jiale Yao <yaojiale02@163.com>
---
drivers/net/wireless/mediatek/mt76/mt7996/mac.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
index 1cba381a78b8..a9558381ad91 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c
@@ -3285,8 +3285,10 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw,
flow->tsf = le64_to_cpu(twt_agrt->twt);
if (mt7996_mcu_twt_agrt_update(dev, &msta->vif->deflink, flow,
- MCU_TWT_AGRT_ADD))
+ MCU_TWT_AGRT_ADD)) {
+ list_del(&flow->list);
goto unlock;
+ }
setup_cmd = TWT_SETUP_CMD_ACCEPT;
dev->twt.table_mask |= BIT(table_id);
--
2.34.1
^ permalink raw reply [flat|nested] 3+ messages in thread* [PATCH 2/2] wifi: mt76: mt7996: clear all link WCIDs before RCU removal
2026-10-18 10:47 [PATCH 0/2] wifi: mt76: mt7996: fix TWT and MLO teardown races Jiale Yao
2026-10-18 10:47 ` [PATCH 1/2] wifi: mt76: mt7996: unlink TWT flow if MCU rejects agreement Jiale Yao
@ 2026-10-18 10:47 ` Jiale Yao
1 sibling, 0 replies; 3+ messages in thread
From: Jiale Yao @ 2026-10-18 10:47 UTC (permalink / raw)
To: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
Sean Wang, Matthias Brugger, AngeloGioacchino Del Regno,
linux-wireless, linux-kernel, linux-arm-kernel, linux-mediatek
Cc: Jiale Yao
mt7996 registers a separate WCID pointer for every link of an MLD
station. The generic mt76 sta_pre_rcu_remove callback only clears the
WCID embedded at the start of sta->drv_priv.
The remaining link WCIDs can therefore still be acquired by the RX path
after station removal starts and outlive the synchronization performed
by mac80211, resulting in a use-after-free.
Add an mt7996 callback that clears every allocated station link WCID
before mac80211 waits for network readers.
Fixes: a9eae65d97f3 ("wifi: mt76: mt7996: Export MLO AP capabilities to mac80211")
Signed-off-by: Jiale Yao <yaojiale02@163.com>
---
.../net/wireless/mediatek/mt76/mt7996/main.c | 27 ++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
index dd7a01c02a7c..dc7401319ed5 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c
@@ -1510,6 +1510,31 @@ mt7996_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
return mt7996_mac_sta_event(dev, vif, sta, ev);
}
+static void
+mt7996_sta_pre_rcu_remove(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv;
+ struct mt7996_dev *dev = mt7996_hw_dev(hw);
+ struct mt7996_sta_link *msta_link;
+ unsigned int link_id;
+
+ mutex_lock(&dev->mt76.mutex);
+ spin_lock_bh(&dev->mt76.status_lock);
+
+ for (link_id = 0; link_id < ARRAY_SIZE(msta->link); link_id++) {
+ msta_link = mt7996_sta_link_protected(dev, msta, link_id);
+ if (!msta_link)
+ continue;
+
+ rcu_assign_pointer(dev->mt76.wcid[msta_link->wcid.idx], NULL);
+ }
+
+ spin_unlock_bh(&dev->mt76.status_lock);
+ mutex_unlock(&dev->mt76.mutex);
+}
+
static void mt7996_tx(struct ieee80211_hw *hw,
struct ieee80211_tx_control *control,
struct sk_buff *skb)
@@ -2568,7 +2593,7 @@ const struct ieee80211_ops mt7996_ops = {
.vif_cfg_changed = mt7996_vif_cfg_changed,
.link_info_changed = mt7996_link_info_changed,
.sta_state = mt7996_sta_state,
- .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
+ .sta_pre_rcu_remove = mt7996_sta_pre_rcu_remove,
.link_sta_rc_update = mt7996_link_sta_rc_update,
.set_key = mt7996_set_key,
.ampdu_action = mt7996_ampdu_action,
--
2.34.1
^ permalink raw reply [flat|nested] 3+ messages in thread