Skip to content

Commit 895cccf

Browse files
pchelkin91gregkh
authored andcommitted
wifi: rtw89: fix use-after-free in rtw89_core_tx_kick_off_and_wait()
[ Upstream commit 3e31a6b ] There is a bug observed when rtw89_core_tx_kick_off_and_wait() tries to access already freed skb_data: BUG: KFENCE: use-after-free write in rtw89_core_tx_kick_off_and_wait drivers/net/wireless/realtek/rtw89/core.c:1110 CPU: 6 UID: 0 PID: 41377 Comm: kworker/u64:24 Not tainted 6.17.0-rc1+ #1 PREEMPT(lazy) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS edk2-20250523-14.fc42 05/23/2025 Workqueue: events_unbound cfg80211_wiphy_work [cfg80211] Use-after-free write at 0x0000000020309d9d (in kfence-#251): rtw89_core_tx_kick_off_and_wait drivers/net/wireless/realtek/rtw89/core.c:1110 rtw89_core_scan_complete drivers/net/wireless/realtek/rtw89/core.c:5338 rtw89_hw_scan_complete_cb drivers/net/wireless/realtek/rtw89/fw.c:7979 rtw89_chanctx_proceed_cb drivers/net/wireless/realtek/rtw89/chan.c:3165 rtw89_chanctx_proceed drivers/net/wireless/realtek/rtw89/chan.h:141 rtw89_hw_scan_complete drivers/net/wireless/realtek/rtw89/fw.c:8012 rtw89_mac_c2h_scanofld_rsp drivers/net/wireless/realtek/rtw89/mac.c:5059 rtw89_fw_c2h_work drivers/net/wireless/realtek/rtw89/fw.c:6758 process_one_work kernel/workqueue.c:3241 worker_thread kernel/workqueue.c:3400 kthread kernel/kthread.c:463 ret_from_fork arch/x86/kernel/process.c:154 ret_from_fork_asm arch/x86/entry/entry_64.S:258 kfence-#251: 0x0000000056e2393d-0x000000009943cb62, size=232, cache=skbuff_head_cache allocated by task 41377 on cpu 6 at 77869.159548s (0.009551s ago): __alloc_skb net/core/skbuff.c:659 __netdev_alloc_skb net/core/skbuff.c:734 ieee80211_nullfunc_get net/mac80211/tx.c:5844 rtw89_core_send_nullfunc drivers/net/wireless/realtek/rtw89/core.c:3431 rtw89_core_scan_complete drivers/net/wireless/realtek/rtw89/core.c:5338 rtw89_hw_scan_complete_cb drivers/net/wireless/realtek/rtw89/fw.c:7979 rtw89_chanctx_proceed_cb drivers/net/wireless/realtek/rtw89/chan.c:3165 rtw89_chanctx_proceed drivers/net/wireless/realtek/rtw89/chan.c:3194 rtw89_hw_scan_complete drivers/net/wireless/realtek/rtw89/fw.c:8012 rtw89_mac_c2h_scanofld_rsp drivers/net/wireless/realtek/rtw89/mac.c:5059 rtw89_fw_c2h_work drivers/net/wireless/realtek/rtw89/fw.c:6758 process_one_work kernel/workqueue.c:3241 worker_thread kernel/workqueue.c:3400 kthread kernel/kthread.c:463 ret_from_fork arch/x86/kernel/process.c:154 ret_from_fork_asm arch/x86/entry/entry_64.S:258 freed by task 1045 on cpu 9 at 77869.168393s (0.001557s ago): ieee80211_tx_status_skb net/mac80211/status.c:1117 rtw89_pci_release_txwd_skb drivers/net/wireless/realtek/rtw89/pci.c:564 rtw89_pci_release_tx_skbs.isra.0 drivers/net/wireless/realtek/rtw89/pci.c:651 rtw89_pci_release_tx drivers/net/wireless/realtek/rtw89/pci.c:676 rtw89_pci_napi_poll drivers/net/wireless/realtek/rtw89/pci.c:4238 __napi_poll net/core/dev.c:7495 net_rx_action net/core/dev.c:7557 net/core/dev.c:7684 handle_softirqs kernel/softirq.c:580 do_softirq.part.0 kernel/softirq.c:480 __local_bh_enable_ip kernel/softirq.c:407 rtw89_pci_interrupt_threadfn drivers/net/wireless/realtek/rtw89/pci.c:927 irq_thread_fn kernel/irq/manage.c:1133 irq_thread kernel/irq/manage.c:1257 kthread kernel/kthread.c:463 ret_from_fork arch/x86/kernel/process.c:154 ret_from_fork_asm arch/x86/entry/entry_64.S:258 It is a consequence of a race between the waiting and the signaling side of the completion: Waiting thread Completing thread rtw89_core_tx_kick_off_and_wait() rcu_assign_pointer(skb_data->wait, wait) /* start waiting */ wait_for_completion_timeout() rtw89_pci_tx_status() rtw89_core_tx_wait_complete() rcu_read_lock() /* signals completion and * proceeds further */ complete(&wait->completion) rcu_read_unlock() ... /* frees skb_data */ ieee80211_tx_status_ni() /* returns (exit status doesn't matter) */ wait_for_completion_timeout() ... /* accesses the already freed skb_data */ rcu_assign_pointer(skb_data->wait, NULL) The completing side might proceed and free the underlying skb even before the waiting side is fully awoken and run to execution. Actually the race happens regardless of wait_for_completion_timeout() exit status, e.g. the waiting side may hit a timeout and the concurrent completing side is still able to free the skb. Skbs which are sent by rtw89_core_tx_kick_off_and_wait() are owned by the driver. They don't come from core ieee80211 stack so no need to pass them to ieee80211_tx_status_ni() on completing side. Introduce a work function which will act as a garbage collector for rtw89_tx_wait_info objects and the associated skbs. Thus no potentially heavy locks are required on the completing side. Found by Linux Verification Center (linuxtesting.org). Fixes: 1ae5ca6 ("wifi: rtw89: add function to wait for completion of TX skbs") Cc: [email protected] Suggested-by: Zong-Zhe Yang <[email protected]> Signed-off-by: Fedor Pchelkin <[email protected]> Acked-by: Ping-Ke Shih <[email protected]> Signed-off-by: Ping-Ke Shih <[email protected]> Link: https://patch.msgid.link/[email protected] [ added wiphy variable declarations ] Signed-off-by: Sasha Levin <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent a9152eb commit 895cccf

File tree

4 files changed

+63
-9
lines changed

4 files changed

+63
-9
lines changed

drivers/net/wireless/realtek/rtw89/core.c

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,14 @@ rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev,
960960
}
961961
}
962962

963+
static void rtw89_tx_wait_work(struct wiphy *wiphy, struct wiphy_work *work)
964+
{
965+
struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev,
966+
tx_wait_work.work);
967+
968+
rtw89_tx_wait_list_clear(rtwdev);
969+
}
970+
963971
void rtw89_core_tx_kick_off(struct rtw89_dev *rtwdev, u8 qsel)
964972
{
965973
u8 ch_dma;
@@ -977,25 +985,32 @@ int rtw89_core_tx_kick_off_and_wait(struct rtw89_dev *rtwdev, struct sk_buff *sk
977985
unsigned long time_left;
978986
int ret = 0;
979987

988+
lockdep_assert_wiphy(rtwdev->hw->wiphy);
989+
980990
wait = kzalloc(sizeof(*wait), GFP_KERNEL);
981991
if (!wait) {
982992
rtw89_core_tx_kick_off(rtwdev, qsel);
983993
return 0;
984994
}
985995

986996
init_completion(&wait->completion);
997+
wait->skb = skb;
987998
rcu_assign_pointer(skb_data->wait, wait);
988999

9891000
rtw89_core_tx_kick_off(rtwdev, qsel);
9901001
time_left = wait_for_completion_timeout(&wait->completion,
9911002
msecs_to_jiffies(timeout));
992-
if (time_left == 0)
993-
ret = -ETIMEDOUT;
994-
else if (!wait->tx_done)
995-
ret = -EAGAIN;
9961003

997-
rcu_assign_pointer(skb_data->wait, NULL);
998-
kfree_rcu(wait, rcu_head);
1004+
if (time_left == 0) {
1005+
ret = -ETIMEDOUT;
1006+
list_add_tail(&wait->list, &rtwdev->tx_waits);
1007+
wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->tx_wait_work,
1008+
RTW89_TX_WAIT_WORK_TIMEOUT);
1009+
} else {
1010+
if (!wait->tx_done)
1011+
ret = -EAGAIN;
1012+
rtw89_tx_wait_release(wait);
1013+
}
9991014

10001015
return ret;
10011016
}
@@ -4419,6 +4434,7 @@ int rtw89_core_start(struct rtw89_dev *rtwdev)
44194434
void rtw89_core_stop(struct rtw89_dev *rtwdev)
44204435
{
44214436
struct rtw89_btc *btc = &rtwdev->btc;
4437+
struct wiphy *wiphy = rtwdev->hw->wiphy;
44224438

44234439
/* Prvent to stop twice; enter_ips and ops_stop */
44244440
if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags))
@@ -4437,6 +4453,7 @@ void rtw89_core_stop(struct rtw89_dev *rtwdev)
44374453
cancel_work_sync(&btc->dhcp_notify_work);
44384454
cancel_work_sync(&btc->icmp_notify_work);
44394455
cancel_delayed_work_sync(&rtwdev->txq_reinvoke_work);
4456+
wiphy_delayed_work_cancel(wiphy, &rtwdev->tx_wait_work);
44404457
cancel_delayed_work_sync(&rtwdev->track_work);
44414458
cancel_delayed_work_sync(&rtwdev->chanctx_work);
44424459
cancel_delayed_work_sync(&rtwdev->coex_act1_work);
@@ -4657,6 +4674,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)
46574674
continue;
46584675
INIT_LIST_HEAD(&rtwdev->scan_info.pkt_list[band]);
46594676
}
4677+
INIT_LIST_HEAD(&rtwdev->tx_waits);
46604678
INIT_WORK(&rtwdev->ba_work, rtw89_core_ba_work);
46614679
INIT_WORK(&rtwdev->txq_work, rtw89_core_txq_work);
46624680
INIT_DELAYED_WORK(&rtwdev->txq_reinvoke_work, rtw89_core_txq_reinvoke_work);
@@ -4666,6 +4684,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)
46664684
INIT_DELAYED_WORK(&rtwdev->coex_bt_devinfo_work, rtw89_coex_bt_devinfo_work);
46674685
INIT_DELAYED_WORK(&rtwdev->coex_rfk_chk_work, rtw89_coex_rfk_chk_work);
46684686
INIT_DELAYED_WORK(&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work);
4687+
wiphy_delayed_work_init(&rtwdev->tx_wait_work, rtw89_tx_wait_work);
46694688
INIT_DELAYED_WORK(&rtwdev->forbid_ba_work, rtw89_forbid_ba_work);
46704689
INIT_DELAYED_WORK(&rtwdev->antdiv_work, rtw89_phy_antdiv_work);
46714690
rtwdev->txq_wq = alloc_workqueue("rtw89_tx_wq", WQ_UNBOUND | WQ_HIGHPRI, 0);

drivers/net/wireless/realtek/rtw89/core.h

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3406,9 +3406,12 @@ struct rtw89_phy_rate_pattern {
34063406
bool enable;
34073407
};
34083408

3409+
#define RTW89_TX_WAIT_WORK_TIMEOUT msecs_to_jiffies(500)
34093410
struct rtw89_tx_wait_info {
34103411
struct rcu_head rcu_head;
3412+
struct list_head list;
34113413
struct completion completion;
3414+
struct sk_buff *skb;
34123415
bool tx_done;
34133416
};
34143417

@@ -5539,6 +5542,9 @@ struct rtw89_dev {
55395542
/* used to protect rpwm */
55405543
spinlock_t rpwm_lock;
55415544

5545+
struct list_head tx_waits;
5546+
struct wiphy_delayed_work tx_wait_work;
5547+
55425548
struct rtw89_cam_info cam_info;
55435549

55445550
struct sk_buff_head c2h_queue;
@@ -5735,6 +5741,26 @@ u8 rtw89_sta_link_inst_get_index(struct rtw89_sta_link *rtwsta_link)
57355741
return rtwsta_link - rtwsta->links_inst;
57365742
}
57375743

5744+
static inline void rtw89_tx_wait_release(struct rtw89_tx_wait_info *wait)
5745+
{
5746+
dev_kfree_skb_any(wait->skb);
5747+
kfree_rcu(wait, rcu_head);
5748+
}
5749+
5750+
static inline void rtw89_tx_wait_list_clear(struct rtw89_dev *rtwdev)
5751+
{
5752+
struct rtw89_tx_wait_info *wait, *tmp;
5753+
5754+
lockdep_assert_wiphy(rtwdev->hw->wiphy);
5755+
5756+
list_for_each_entry_safe(wait, tmp, &rtwdev->tx_waits, list) {
5757+
if (!completion_done(&wait->completion))
5758+
continue;
5759+
list_del(&wait->list);
5760+
rtw89_tx_wait_release(wait);
5761+
}
5762+
}
5763+
57385764
static inline int rtw89_hci_tx_write(struct rtw89_dev *rtwdev,
57395765
struct rtw89_core_tx_request *tx_req)
57405766
{
@@ -5744,6 +5770,7 @@ static inline int rtw89_hci_tx_write(struct rtw89_dev *rtwdev,
57445770
static inline void rtw89_hci_reset(struct rtw89_dev *rtwdev)
57455771
{
57465772
rtwdev->hci.ops->reset(rtwdev);
5773+
rtw89_tx_wait_list_clear(rtwdev);
57475774
}
57485775

57495776
static inline int rtw89_hci_start(struct rtw89_dev *rtwdev)
@@ -6745,23 +6772,27 @@ static inline struct sk_buff *rtw89_alloc_skb_for_rx(struct rtw89_dev *rtwdev,
67456772
return dev_alloc_skb(length);
67466773
}
67476774

6748-
static inline void rtw89_core_tx_wait_complete(struct rtw89_dev *rtwdev,
6775+
static inline bool rtw89_core_tx_wait_complete(struct rtw89_dev *rtwdev,
67496776
struct rtw89_tx_skb_data *skb_data,
67506777
bool tx_done)
67516778
{
67526779
struct rtw89_tx_wait_info *wait;
6780+
bool ret = false;
67536781

67546782
rcu_read_lock();
67556783

67566784
wait = rcu_dereference(skb_data->wait);
67576785
if (!wait)
67586786
goto out;
67596787

6788+
ret = true;
67606789
wait->tx_done = tx_done;
6761-
complete(&wait->completion);
6790+
/* Don't access skb anymore after completion */
6791+
complete_all(&wait->completion);
67626792

67636793
out:
67646794
rcu_read_unlock();
6795+
return ret;
67656796
}
67666797

67676798
static inline bool rtw89_is_mlo_1_1(struct rtw89_dev *rtwdev)

drivers/net/wireless/realtek/rtw89/pci.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,8 @@ static void rtw89_pci_tx_status(struct rtw89_dev *rtwdev,
458458
struct rtw89_tx_skb_data *skb_data = RTW89_TX_SKB_CB(skb);
459459
struct ieee80211_tx_info *info;
460460

461-
rtw89_core_tx_wait_complete(rtwdev, skb_data, tx_status == RTW89_TX_DONE);
461+
if (rtw89_core_tx_wait_complete(rtwdev, skb_data, tx_status == RTW89_TX_DONE))
462+
return;
462463

463464
info = IEEE80211_SKB_CB(skb);
464465
ieee80211_tx_info_clear_status(info);

drivers/net/wireless/realtek/rtw89/ser.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ static void ser_l1_reset_pre_st_hdl(struct rtw89_ser *ser, u8 evt)
484484
static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt)
485485
{
486486
struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser);
487+
struct wiphy *wiphy = rtwdev->hw->wiphy;
487488

488489
switch (evt) {
489490
case SER_EV_STATE_IN:
@@ -496,7 +497,9 @@ static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt)
496497
}
497498

498499
drv_stop_rx(ser);
500+
wiphy_lock(wiphy);
499501
drv_trx_reset(ser);
502+
wiphy_unlock(wiphy);
500503

501504
/* wait m3 */
502505
hal_send_m2_event(ser);

0 commit comments

Comments
 (0)