]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: rtw88: usb: fix memory leaks on USB write failures
authorLuka Gejak <luka.gejak@linux.dev>
Mon, 18 May 2026 14:23:11 +0000 (16:23 +0200)
committerPing-Ke Shih <pkshih@realtek.com>
Mon, 25 May 2026 06:33:43 +0000 (14:33 +0800)
When rtw_usb_write_port() fails to submit a USB Request Block (URB)
(e.g., due to device disconnect or ENOMEM), the completion callback is
never executed.

Currently, the driver ignores the return value of rtw_usb_write_port()
in rtw_usb_write_data() and rtw_usb_tx_agg_skb(). Because these
functions rely on the completion callback to free the socket buffers
(skbs) and the transaction control block (txcb), a submission failure
results in:
1. A memory leak of the allocated skb in rtw_usb_write_data().
2. A memory leak of the txcb structure and all aggregated skbs in
   rtw_usb_tx_agg_skb().

Fix this by checking the return value of rtw_usb_write_port(). If it
fails, explicitly free the skb in rtw_usb_write_data(), and properly
purge the tx_ack_queue and free the txcb in rtw_usb_tx_agg_skb().

The issue was discovered in practice during device disconnect/reconnect
scenarios and memory pressure conditions. Tested by verifying normal TX
operation continues after the fix without regressions.

Fixes: a82dfd33d123 ("wifi: rtw88: Add common USB chip support")
Cc: stable@vger.kernel.org
Acked-by: Ping-Ke Shih <pkshih@realtek.com>
Tested-by: Luka Gejak <luka.gejak@linux.dev>
Signed-off-by: Luka Gejak <luka.gejak@linux.dev>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20260518142311.10328-2-luka.gejak@linux.dev
drivers/net/wireless/realtek/rtw88/usb.c

index 718940ebba316115b75d924f6cf3782a86f27c6c..1a0bdbf52cb0d464b43fcbbaac0b8aa3e0329ba0 100644 (file)
@@ -399,6 +399,7 @@ static bool rtw_usb_tx_agg_skb(struct rtw_usb *rtwusb, struct sk_buff_head *list
        int agg_num = 0;
        unsigned int align_next = 0;
        u8 qsel;
+       int ret;
 
        if (skb_queue_empty(list))
                return false;
@@ -456,7 +457,13 @@ queue:
        tx_desc = (struct rtw_tx_desc *)skb_head->data;
        qsel = le32_get_bits(tx_desc->w1, RTW_TX_DESC_W1_QSEL);
 
-       rtw_usb_write_port(rtwdev, qsel, skb_head, rtw_usb_write_port_tx_complete, txcb);
+       ret = rtw_usb_write_port(rtwdev, qsel, skb_head,
+                                rtw_usb_write_port_tx_complete, txcb);
+       if (ret) {
+               ieee80211_purge_tx_queue(rtwdev->hw, &txcb->tx_ack_queue);
+               kfree(txcb);
+               return false;
+       }
 
        return true;
 }
@@ -518,8 +525,10 @@ static int rtw_usb_write_data(struct rtw_dev *rtwdev,
 
        ret = rtw_usb_write_port(rtwdev, qsel, skb,
                                 rtw_usb_write_port_complete, skb);
-       if (unlikely(ret))
+       if (unlikely(ret)) {
                rtw_err(rtwdev, "failed to do USB write, ret=%d\n", ret);
+               dev_kfree_skb_any(skb);
+       }
 
        return ret;
 }