mt76: mt7615: fix skb use-after-free on mac reset
authorFelix Fietkau <nbd@nbd.name>
Wed, 21 Jul 2021 05:23:46 +0000 (07:23 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 18 Nov 2021 18:15:59 +0000 (19:15 +0100)
commit b5cd1fd6043bbb7c5810067b5f93f3016bfd8a6f upstream.

When clearing all existing pending tx slots, mt76_tx_complete_skb needs to
be used to free the skbs, to ensure that they are cleared from the status
list as well.

Cc: stable@vger.kernel.org
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/net/wireless/mediatek/mt76/mt7615/mac.c

index ff3f85e..a83e48c 100644 (file)
@@ -1494,32 +1494,41 @@ out:
 }
 
 static void
-mt7615_mac_tx_free_token(struct mt7615_dev *dev, u16 token)
+mt7615_txwi_free(struct mt7615_dev *dev, struct mt76_txwi_cache *txwi)
 {
        struct mt76_dev *mdev = &dev->mt76;
-       struct mt76_txwi_cache *txwi;
        __le32 *txwi_data;
        u32 val;
        u8 wcid;
 
-       trace_mac_tx_free(dev, token);
-       txwi = mt76_token_put(mdev, token);
-       if (!txwi)
-               return;
+       mt7615_txp_skb_unmap(mdev, txwi);
+       if (!txwi->skb)
+               goto out;
 
        txwi_data = (__le32 *)mt76_get_txwi_ptr(mdev, txwi);
        val = le32_to_cpu(txwi_data[1]);
        wcid = FIELD_GET(MT_TXD1_WLAN_IDX, val);
+       mt76_tx_complete_skb(mdev, wcid, txwi->skb);
 
-       mt7615_txp_skb_unmap(mdev, txwi);
-       if (txwi->skb) {
-               mt76_tx_complete_skb(mdev, wcid, txwi->skb);
-               txwi->skb = NULL;
-       }
-
+out:
+       txwi->skb = NULL;
        mt76_put_txwi(mdev, txwi);
 }
 
+static void
+mt7615_mac_tx_free_token(struct mt7615_dev *dev, u16 token)
+{
+       struct mt76_dev *mdev = &dev->mt76;
+       struct mt76_txwi_cache *txwi;
+
+       trace_mac_tx_free(dev, token);
+       txwi = mt76_token_put(mdev, token);
+       if (!txwi)
+               return;
+
+       mt7615_txwi_free(dev, txwi);
+}
+
 static void mt7615_mac_tx_free(struct mt7615_dev *dev, struct sk_buff *skb)
 {
        struct mt7615_tx_free *free = (struct mt7615_tx_free *)skb->data;
@@ -2026,16 +2035,8 @@ void mt7615_tx_token_put(struct mt7615_dev *dev)
        int id;
 
        spin_lock_bh(&dev->mt76.token_lock);
-       idr_for_each_entry(&dev->mt76.token, txwi, id) {
-               mt7615_txp_skb_unmap(&dev->mt76, txwi);
-               if (txwi->skb) {
-                       struct ieee80211_hw *hw;
-
-                       hw = mt76_tx_status_get_hw(&dev->mt76, txwi->skb);
-                       ieee80211_free_txskb(hw, txwi->skb);
-               }
-               mt76_put_txwi(&dev->mt76, txwi);
-       }
+       idr_for_each_entry(&dev->mt76.token, txwi, id)
+               mt7615_txwi_free(dev, txwi);
        spin_unlock_bh(&dev->mt76.token_lock);
        idr_destroy(&dev->mt76.token);
 }