From: Eason Lai <eason.lai@mediatek.com>
To: <nbd@nbd.name>, <lorenzo@kernel.org>
Cc: <linux-wireless@vger.kernel.org>,
<linux-mediatek@lists.infradead.org>, <kun.wu@mediatek.com>,
<deren.wu@mediatek.com>, <sean.wang@mediatek.com>,
<quan.zhou@mediatek.com>, <ryder.lee@mediatek.com>,
<leon.yen@mediatek.com>, <litien.chang@mediatek.com>,
<jb.tsai@mediatek.com>, <lucid_duck@justthetip.ca>,
<eason.lai@mediatek.com>, <stable@vger.kernel.org>
Subject: [PATCH v1 1/2] wifi: mt76: flush a partial rx A-MSDU when its station is removed
Date: Wed, 28 Oct 2026 10:10:52 +0800 [thread overview]
Message-ID: <20261028021053.2220951-2-eason.lai@mediatek.com> (raw)
In-Reply-To: <20261028021053.2220951-1-eason.lai@mediatek.com>
mt76_rx_release_burst() keeps the first subframe of an A-MSDU in
phy->rx_amsdu[q] until the last subframe arrives, which can take several
polls. mt76_rx_status.wcid in that skb is only valid inside the RCU
section it was filled in.
If the station is removed meanwhile, the wcid is freed together with the
sta, and mt76_rx_convert() and mt76_check_ccmp_pn() use a dangling
pointer once the A-MSDU is released. Neither mt76_rx_aggr_stop() nor
__mt76_sta_remove() looks at rx_amsdu.
Drop a parked A-MSDU that references the wcid in mt76_wcid_cleanup(),
which every driver calls on station removal, and protect rx_amsdu with a
new per-queue lock.
Fixes: cc4b3c139ad3 ("mt76: mt7915: enable hw rx-amsdu de-aggregation")
Signed-off-by: Eason Lai <eason.lai@mediatek.com>
---
drivers/net/wireless/mediatek/mt76/mac80211.c | 43 +++++++++++++++++++
drivers/net/wireless/mediatek/mt76/mt76.h | 1 +
2 files changed, 44 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index c9762082f473..19a2e8a26c3e 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -428,9 +428,12 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw)
{
struct mt76_dev *dev = phy->dev;
struct wiphy *wiphy = hw->wiphy;
+ int i;
INIT_LIST_HEAD(&phy->tx_list);
spin_lock_init(&phy->tx_lock);
+ for (i = 0; i < ARRAY_SIZE(phy->rx_amsdu); i++)
+ spin_lock_init(&phy->rx_amsdu[i].lock);
INIT_DELAYED_WORK(&phy->roc_work, mt76_roc_complete_work);
INIT_DELAYED_WORK(&phy->scan_work, mt76_scan_work);
@@ -974,10 +977,48 @@ void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb)
}
#endif
+ spin_lock(&phy->rx_amsdu[q].lock);
mt76_rx_release_burst(phy, q, skb);
+ spin_unlock(&phy->rx_amsdu[q].lock);
}
EXPORT_SYMBOL_GPL(mt76_rx);
+/* Drop a partially received A-MSDU that still references the wcid of a
+ * station which is being removed.
+ */
+static void mt76_rx_amsdu_flush_wcid(struct mt76_dev *dev,
+ struct mt76_wcid *wcid)
+{
+ int i, q;
+
+ for (i = 0; i < ARRAY_SIZE(dev->phys); i++) {
+ struct mt76_phy *phy = dev->phys[i];
+
+ if (!phy)
+ continue;
+
+ for (q = 0; q < ARRAY_SIZE(phy->rx_amsdu); q++) {
+ struct sk_buff *skb;
+ struct mt76_rx_status *status;
+
+ spin_lock_bh(&phy->rx_amsdu[q].lock);
+ skb = phy->rx_amsdu[q].head;
+ if (skb) {
+ status = (struct mt76_rx_status *)skb->cb;
+ if (status->wcid == wcid) {
+ phy->rx_amsdu[q].head = NULL;
+ phy->rx_amsdu[q].tail = NULL;
+ } else {
+ skb = NULL;
+ }
+ }
+ spin_unlock_bh(&phy->rx_amsdu[q].lock);
+
+ dev_kfree_skb(skb);
+ }
+ }
+}
+
bool mt76_has_tx_pending(struct mt76_phy *phy)
{
struct mt76_queue *q;
@@ -1760,6 +1801,8 @@ void mt76_wcid_cleanup(struct mt76_dev *dev, struct mt76_wcid *wcid)
struct sk_buff_head list;
struct sk_buff *skb;
+ mt76_rx_amsdu_flush_wcid(dev, wcid);
+
mt76_tx_status_lock(dev, &list);
mt76_tx_status_skb_get(dev, wcid, -1, &list);
idr_destroy(&wcid->pktid);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 9e851dea71d9..a4db4a2aaffa 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -913,6 +913,7 @@ struct mt76_phy {
u8 mac_work_count;
struct {
+ spinlock_t lock;
struct sk_buff *head;
struct sk_buff **tail;
u16 seqno;
--
2.45.2
next prev parent reply other threads:[~2026-10-28 2:11 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-28 2:10 [PATCH v1 0/2] wifi: mt76: Fix UAF and SKB leaks in RX A-MSDU path Eason Lai
2026-10-28 2:10 ` Eason Lai [this message]
2026-10-28 2:10 ` [PATCH v1 2/2] wifi: mt76: free a partial rx A-MSDU when the phy is unregistered Eason Lai
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261028021053.2220951-2-eason.lai@mediatek.com \
--to=eason.lai@mediatek.com \
--cc=deren.wu@mediatek.com \
--cc=jb.tsai@mediatek.com \
--cc=kun.wu@mediatek.com \
--cc=leon.yen@mediatek.com \
--cc=linux-mediatek@lists.infradead.org \
--cc=linux-wireless@vger.kernel.org \
--cc=litien.chang@mediatek.com \
--cc=lorenzo@kernel.org \
--cc=lucid_duck@justthetip.ca \
--cc=nbd@nbd.name \
--cc=quan.zhou@mediatek.com \
--cc=ryder.lee@mediatek.com \
--cc=sean.wang@mediatek.com \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox