FAILED: patch "[PATCH] wifi: rtw89: fix use-after-free in" failed to apply to 6.16-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.16-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.16.y
git checkout FETCH_HEAD
git cherry-pick -x 3e31a6bc07312b448fad3b45de578471f86f0e77
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2025100340-dwelling-engross-0738@gregkh' --subject-prefix 'PATCH 6.16.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 3e31a6bc07312b448fad3b45de578471f86f0e77 Mon Sep 17 00:00:00 2001
From: Fedor Pchelkin <[email protected]>
Date: Sat, 20 Sep 2025 00:08:47 +0300
Subject: [PATCH] wifi: rtw89: fix use-after-free in
rtw89_core_tx_kick_off_and_wait()
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).
drivers/net/wireless/realtek/rtw89/core.c +24 -6
@@ -1135,6 +1135,14 @@ rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev, } } +static void rtw89_tx_wait_work (struct wiphy *wiphy,struct wiphy_work *work) +{ + struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, + tx_wait_work.work); + +rtw89_tx_wait_list_clear (rtwdev); +} +void rtw89_core_tx_kick_off (struct rtw89_dev *rtwdev,u8 qsel) {u8 ch_dma;
@@ -1152,6 +1160,8 @@ int rtw89_core_tx_kick_off_and_wait(struct rtw89_dev *rtwdev, struct sk_buff *skunsigned long time_left;int ret =0 ; +lockdep_assert_wiphy (rtwdev->hw->wiphy); + wait =kzalloc (sizeof (*wait),GFP_KERNEL );if (!wait) {rtw89_core_tx_kick_off (rtwdev, qsel);
@@ -1159,18 +1169,23 @@ int rtw89_core_tx_kick_off_and_wait(struct rtw89_dev *rtwdev, struct sk_buff *sk }init_completion (&wait->completion); + wait->skb = skb;rcu_assign_pointer (skb_data->wait, wait);rtw89_core_tx_kick_off (rtwdev, qsel); time_left =wait_for_completion_timeout (&wait->completion,msecs_to_jiffies (timeout)); -if (time_left ==0 ) - ret = -ETIMEDOUT ; -else if (!wait->tx_done) - ret = -EAGAIN ; -rcu_assign_pointer (skb_data->wait,NULL ); -kfree_rcu (wait, rcu_head); +if (time_left ==0 ) { + ret = -ETIMEDOUT ; +list_add_tail (&wait->list, &rtwdev->tx_waits); +wiphy_delayed_work_queue (rtwdev->hw->wiphy, &rtwdev->tx_wait_work, +RTW89_TX_WAIT_WORK_TIMEOUT ); + }else { +if (!wait->tx_done) + ret = -EAGAIN ; +rtw89_tx_wait_release (wait); + }return ret; }
@@ -5548,6 +5563,7 @@ void rtw89_core_stop(struct rtw89_dev *rtwdev)wiphy_work_cancel (wiphy, &btc->dhcp_notify_work);wiphy_work_cancel (wiphy, &btc->icmp_notify_work);cancel_delayed_work_sync (&rtwdev->txq_reinvoke_work); +wiphy_delayed_work_cancel (wiphy, &rtwdev->tx_wait_work);wiphy_delayed_work_cancel (wiphy, &rtwdev->track_work);wiphy_delayed_work_cancel (wiphy, &rtwdev->track_ps_work);wiphy_delayed_work_cancel (wiphy, &rtwdev->chanctx_work);
@@ -5773,6 +5789,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)INIT_LIST_HEAD (&rtwdev->scan_info.pkt_list[band]); }INIT_LIST_HEAD (&rtwdev->scan_info.chan_list); +INIT_LIST_HEAD (&rtwdev->tx_waits);INIT_WORK (&rtwdev->ba_work, rtw89_core_ba_work);INIT_WORK (&rtwdev->txq_work, rtw89_core_txq_work);INIT_DELAYED_WORK (&rtwdev->txq_reinvoke_work, rtw89_core_txq_reinvoke_work);
@@ -5784,6 +5801,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)wiphy_delayed_work_init (&rtwdev->coex_rfk_chk_work, rtw89_coex_rfk_chk_work);wiphy_delayed_work_init (&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work);wiphy_delayed_work_init (&rtwdev->mcc_prepare_done_work, rtw89_mcc_prepare_done_work); +wiphy_delayed_work_init (&rtwdev->tx_wait_work, rtw89_tx_wait_work);INIT_DELAYED_WORK (&rtwdev->forbid_ba_work, rtw89_forbid_ba_work);wiphy_delayed_work_init (&rtwdev->antdiv_work, rtw89_phy_antdiv_work); rtwdev->txq_wq =alloc_workqueue ("rtw89_tx_wq" ,WQ_UNBOUND |WQ_HIGHPRI ,0 );
drivers/net/wireless/realtek/rtw89/core.h +33 -2
@@ -3507,9 +3507,12 @@ struct rtw89_phy_rate_pattern {bool enable; }; +#define RTW89_TX_WAIT_WORK_TIMEOUT msecs_to_jiffies(500)struct rtw89_tx_wait_info {struct rcu_head rcu_head; +struct list_head list;struct completion completion; +struct sk_buff *skb;bool tx_done; };
@@ -6000,6 +6003,9 @@ struct rtw89_dev {/* used to protect rpwm */ spinlock_t rpwm_lock; +struct list_head tx_waits; +struct wiphy_delayed_work tx_wait_work; +struct rtw89_cam_info cam_info;struct sk_buff_head c2h_queue;
@@ -6256,6 +6262,26 @@ rtw89_assoc_link_rcu_dereference(struct rtw89_dev *rtwdev, u8 macid)list_first_entry_or_null (&p->dlink_pool,typeof (*p->links_inst), dlink_schd); \ }) +static inline void rtw89_tx_wait_release (struct rtw89_tx_wait_info *wait) +{ +dev_kfree_skb_any (wait->skb); +kfree_rcu (wait, rcu_head); +} + +static inline void rtw89_tx_wait_list_clear (struct rtw89_dev *rtwdev) +{ +struct rtw89_tx_wait_info *wait, *tmp; + +lockdep_assert_wiphy (rtwdev->hw->wiphy); + +list_for_each_entry_safe (wait, tmp, &rtwdev->tx_waits, list) { +if (!completion_done (&wait->completion)) +continue ; +list_del (&wait->list); +rtw89_tx_wait_release (wait); + } +} +static inline int rtw89_hci_tx_write (struct rtw89_dev *rtwdev,struct rtw89_core_tx_request *tx_req) {
@@ -6265,6 +6291,7 @@ static inline int rtw89_hci_tx_write(struct rtw89_dev *rtwdev,static inline void rtw89_hci_reset (struct rtw89_dev *rtwdev) { rtwdev->hci.ops->reset (rtwdev); +rtw89_tx_wait_list_clear (rtwdev); }static inline int rtw89_hci_start (struct rtw89_dev *rtwdev)
@@ -7345,11 +7372,12 @@ static inline struct sk_buff *rtw89_alloc_skb_for_rx(struct rtw89_dev *rtwdev,return dev_alloc_skb (length); } -static inline void rtw89_core_tx_wait_complete (struct rtw89_dev *rtwdev, +static inline bool rtw89_core_tx_wait_complete (struct rtw89_dev *rtwdev,struct rtw89_tx_skb_data *skb_data,bool tx_done) {struct rtw89_tx_wait_info *wait; +bool ret = false;rcu_read_lock ();
@@ -7357,11 +7385,14 @@ static inline void rtw89_core_tx_wait_complete(struct rtw89_dev *rtwdev,if (!wait) goto out; + ret = true; wait->tx_done = tx_done; -complete (&wait->completion); +/* Don't access skb anymore after completion */ +complete_all (&wait->completion); out:rcu_read_unlock (); +return ret; }static inline bool rtw89_is_mlo_1_1 (struct rtw89_dev *rtwdev)
drivers/net/wireless/realtek/rtw89/pci.c +2 -1
@@ -464,7 +464,8 @@ static void rtw89_pci_tx_status(struct rtw89_dev *rtwdev,struct rtw89_tx_skb_data *skb_data =RTW89_TX_SKB_CB (skb);struct ieee80211_tx_info *info; -rtw89_core_tx_wait_complete (rtwdev, skb_data, tx_status ==RTW89_TX_DONE ); +if (rtw89_core_tx_wait_complete (rtwdev, skb_data, tx_status ==RTW89_TX_DONE )) +return ; info =IEEE80211_SKB_CB (skb);ieee80211_tx_info_clear_status (info);
drivers/net/wireless/realtek/rtw89/ser.c +2 -0
@@ -502,7 +502,9 @@ static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt) }drv_stop_rx (ser); +wiphy_lock (wiphy);drv_trx_reset (ser); +wiphy_unlock (wiphy);/* wait m3 */ hal_send_m2_event (ser);
Sasha Levin
maintainerPATCH 1/2 wifi: rtw89: mcc: stop TX during MCC prepare
From: Chih-Kang Chang <[email protected]>
[ Upstream commit 182c7ff8b87e4edbb2227ede39ae0952da7a0f4a ]
Stop TX during the MCC configuration period to prevent packet leakage. The stop time is defined as 'start_tsf - tsf', which means the duration from when MCC configuration begins until MCC starts.
Stable-dep-of: 3e31a6bc0731 ("wifi: rtw89: fix use-after-free in rtw89_core_tx_kick_off_and_wait()")
--- drivers/net/wireless/realtek/rtw89/chan.c | 35 +++++++++++++++++++++++ drivers/net/wireless/realtek/rtw89/chan.h | 2 ++ drivers/net/wireless/realtek/rtw89/core.c | 2 ++ drivers/net/wireless/realtek/rtw89/core.h | 2 ++ 4 files changed, 41 insertions(+)
drivers/net/wireless/realtek/rtw89/chan.c +35 -0
@@ -1595,6 +1595,35 @@ static bool rtw89_mcc_duration_decision_on_bt(struct rtw89_dev *rtwdev)return false; } +void rtw89_mcc_prepare_done_work (struct wiphy *wiphy,struct wiphy_work *work) +{ + struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, + mcc_prepare_done_work.work); + +lockdep_assert_wiphy (wiphy); + +ieee80211_wake_queues (rtwdev->hw); +} + +static void rtw89_mcc_prepare (struct rtw89_dev *rtwdev,bool start) +{ +struct rtw89_mcc_info *mcc = &rtwdev->mcc; +struct rtw89_mcc_config *config = &mcc->config; + +if (start) { +ieee80211_stop_queues (rtwdev->hw); + +wiphy_delayed_work_queue (rtwdev->hw->wiphy, + &rtwdev->mcc_prepare_done_work, +usecs_to_jiffies (config->prepare_delay)); + }else { +wiphy_delayed_work_queue (rtwdev->hw->wiphy, + &rtwdev->mcc_prepare_done_work,0 ); +wiphy_delayed_work_flush (rtwdev->hw->wiphy, + &rtwdev->mcc_prepare_done_work); + } +} +static int rtw89_mcc_fill_start_tsf (struct rtw89_dev *rtwdev) {struct rtw89_mcc_info *mcc = &rtwdev->mcc;
@@ -1630,6 +1659,8 @@ static int rtw89_mcc_fill_start_tsf(struct rtw89_dev *rtwdev) config->start_tsf = start_tsf; config->start_tsf_in_aux_domain = tsf_aux + start_tsf - tsf; + config->prepare_delay = start_tsf - tsf; +return 0 ; }
@@ -2219,6 +2250,8 @@ static int rtw89_mcc_start(struct rtw89_dev *rtwdev)rtw89_chanctx_notify (rtwdev,RTW89_CHANCTX_STATE_MCC_START );rtw89_mcc_start_beacon_noa (rtwdev); + +rtw89_mcc_prepare (rtwdev, true);return 0 ; }
@@ -2307,6 +2340,8 @@ static void rtw89_mcc_stop(struct rtw89_dev *rtwdev,rtw89_chanctx_notify (rtwdev,RTW89_CHANCTX_STATE_MCC_STOP );rtw89_mcc_stop_beacon_noa (rtwdev); + +rtw89_mcc_prepare (rtwdev, false); }static int rtw89_mcc_update (struct rtw89_dev *rtwdev)
drivers/net/wireless/realtek/rtw89/chan.h +2 -0
@@ -129,6 +129,8 @@ const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev,#define rtw89_mgnt_chan_get (rtwdev, link_index) \ __rtw89_mgnt_chan_get(rtwdev, __func__, link_index) +void rtw89_mcc_prepare_done_work (struct wiphy *wiphy,struct wiphy_work *work); +int rtw89_chanctx_ops_add (struct rtw89_dev *rtwdev,struct ieee80211_chanctx_conf *ctx); void rtw89_chanctx_ops_remove(struct rtw89_dev *rtwdev,
drivers/net/wireless/realtek/rtw89/core.c +2 -0
@@ -4816,6 +4816,7 @@ void rtw89_core_stop(struct rtw89_dev *rtwdev)wiphy_delayed_work_cancel (wiphy, &rtwdev->coex_bt_devinfo_work);wiphy_delayed_work_cancel (wiphy, &rtwdev->coex_rfk_chk_work);wiphy_delayed_work_cancel (wiphy, &rtwdev->cfo_track_work); +wiphy_delayed_work_cancel (wiphy, &rtwdev->mcc_prepare_done_work);cancel_delayed_work_sync (&rtwdev->forbid_ba_work);wiphy_delayed_work_cancel (wiphy, &rtwdev->antdiv_work);
@@ -5042,6 +5043,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)wiphy_delayed_work_init (&rtwdev->coex_bt_devinfo_work, rtw89_coex_bt_devinfo_work);wiphy_delayed_work_init (&rtwdev->coex_rfk_chk_work, rtw89_coex_rfk_chk_work);wiphy_delayed_work_init (&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work); +wiphy_delayed_work_init (&rtwdev->mcc_prepare_done_work, rtw89_mcc_prepare_done_work);INIT_DELAYED_WORK (&rtwdev->forbid_ba_work, rtw89_forbid_ba_work);wiphy_delayed_work_init (&rtwdev->antdiv_work, rtw89_phy_antdiv_work); rtwdev->txq_wq =alloc_workqueue ("rtw89_tx_wq" ,WQ_UNBOUND |WQ_HIGHPRI ,0 );
drivers/net/wireless/realtek/rtw89/core.h +2 -0
@@ -5728,6 +5728,7 @@ struct rtw89_mcc_config {struct rtw89_mcc_sync sync;u64 start_tsf;u64 start_tsf_in_aux_domain; +u64 prepare_delay;u16 mcc_interval;/* TU */ u16 beacon_offset;/* TU */ };
@@ -5858,6 +5859,7 @@ struct rtw89_dev {struct wiphy_delayed_work coex_bt_devinfo_work;struct wiphy_delayed_work coex_rfk_chk_work;struct wiphy_delayed_work cfo_track_work; +struct wiphy_delayed_work mcc_prepare_done_work;struct delayed_work forbid_ba_work;struct wiphy_delayed_work antdiv_work;struct rtw89_ppdu_sts_info ppdu_sts;
Signature · 1 line
2.51.0
Sasha Levin
maintainerPATCH 2/2 wifi: rtw89: fix use-after-free in rtw89_core_tx_kick_off_and_wait()
From: Fedor Pchelkin <[email protected]>
[ Upstream commit 3e31a6bc07312b448fad3b45de578471f86f0e77 ]
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).
--- drivers/net/wireless/realtek/rtw89/core.c | 30 +++++++++++++++---- drivers/net/wireless/realtek/rtw89/core.h | 35 +++++++++++++++++++++-- drivers/net/wireless/realtek/rtw89/pci.c | 3 +- drivers/net/wireless/realtek/rtw89/ser.c | 2 ++ 4 files changed, 61 insertions(+), 9 deletions(-)
drivers/net/wireless/realtek/rtw89/core.c +24 -6
@@ -1050,6 +1050,14 @@ rtw89_core_tx_update_desc_info(struct rtw89_dev *rtwdev, } } +static void rtw89_tx_wait_work (struct wiphy *wiphy,struct wiphy_work *work) +{ + struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, + tx_wait_work.work); + +rtw89_tx_wait_list_clear (rtwdev); +} +void rtw89_core_tx_kick_off (struct rtw89_dev *rtwdev,u8 qsel) {u8 ch_dma;
@@ -1067,6 +1075,8 @@ int rtw89_core_tx_kick_off_and_wait(struct rtw89_dev *rtwdev, struct sk_buff *skunsigned long time_left;int ret =0 ; +lockdep_assert_wiphy (rtwdev->hw->wiphy); + wait =kzalloc (sizeof (*wait),GFP_KERNEL );if (!wait) {rtw89_core_tx_kick_off (rtwdev, qsel);
@@ -1074,18 +1084,23 @@ int rtw89_core_tx_kick_off_and_wait(struct rtw89_dev *rtwdev, struct sk_buff *sk }init_completion (&wait->completion); + wait->skb = skb;rcu_assign_pointer (skb_data->wait, wait);rtw89_core_tx_kick_off (rtwdev, qsel); time_left =wait_for_completion_timeout (&wait->completion,msecs_to_jiffies (timeout)); -if (time_left ==0 ) - ret = -ETIMEDOUT ; -else if (!wait->tx_done) - ret = -EAGAIN ; -rcu_assign_pointer (skb_data->wait,NULL ); -kfree_rcu (wait, rcu_head); +if (time_left ==0 ) { + ret = -ETIMEDOUT ; +list_add_tail (&wait->list, &rtwdev->tx_waits); +wiphy_delayed_work_queue (rtwdev->hw->wiphy, &rtwdev->tx_wait_work, +RTW89_TX_WAIT_WORK_TIMEOUT ); + }else { +if (!wait->tx_done) + ret = -EAGAIN ; +rtw89_tx_wait_release (wait); + }return ret; }
@@ -4810,6 +4825,7 @@ void rtw89_core_stop(struct rtw89_dev *rtwdev)wiphy_work_cancel (wiphy, &btc->dhcp_notify_work);wiphy_work_cancel (wiphy, &btc->icmp_notify_work);cancel_delayed_work_sync (&rtwdev->txq_reinvoke_work); +wiphy_delayed_work_cancel (wiphy, &rtwdev->tx_wait_work);wiphy_delayed_work_cancel (wiphy, &rtwdev->track_work);wiphy_delayed_work_cancel (wiphy, &rtwdev->chanctx_work);wiphy_delayed_work_cancel (wiphy, &rtwdev->coex_act1_work);
@@ -5034,6 +5050,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)INIT_LIST_HEAD (&rtwdev->scan_info.pkt_list[band]); }INIT_LIST_HEAD (&rtwdev->scan_info.chan_list); +INIT_LIST_HEAD (&rtwdev->tx_waits);INIT_WORK (&rtwdev->ba_work, rtw89_core_ba_work);INIT_WORK (&rtwdev->txq_work, rtw89_core_txq_work);INIT_DELAYED_WORK (&rtwdev->txq_reinvoke_work, rtw89_core_txq_reinvoke_work);
@@ -5044,6 +5061,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev)wiphy_delayed_work_init (&rtwdev->coex_rfk_chk_work, rtw89_coex_rfk_chk_work);wiphy_delayed_work_init (&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work);wiphy_delayed_work_init (&rtwdev->mcc_prepare_done_work, rtw89_mcc_prepare_done_work); +wiphy_delayed_work_init (&rtwdev->tx_wait_work, rtw89_tx_wait_work);INIT_DELAYED_WORK (&rtwdev->forbid_ba_work, rtw89_forbid_ba_work);wiphy_delayed_work_init (&rtwdev->antdiv_work, rtw89_phy_antdiv_work); rtwdev->txq_wq =alloc_workqueue ("rtw89_tx_wq" ,WQ_UNBOUND |WQ_HIGHPRI ,0 );
drivers/net/wireless/realtek/rtw89/core.h +33 -2
@@ -3429,9 +3429,12 @@ struct rtw89_phy_rate_pattern {bool enable; }; +#define RTW89_TX_WAIT_WORK_TIMEOUT msecs_to_jiffies(500)struct rtw89_tx_wait_info {struct rcu_head rcu_head; +struct list_head list;struct completion completion; +struct sk_buff *skb;bool tx_done; };
@@ -5802,6 +5805,9 @@ struct rtw89_dev {/* used to protect rpwm */ spinlock_t rpwm_lock; +struct list_head tx_waits; +struct wiphy_delayed_work tx_wait_work; +struct rtw89_cam_info cam_info;struct sk_buff_head c2h_queue;
@@ -6056,6 +6062,26 @@ rtw89_assoc_link_rcu_dereference(struct rtw89_dev *rtwdev, u8 macid)list_first_entry_or_null (&p->dlink_pool,typeof (*p->links_inst), dlink_schd); \ }) +static inline void rtw89_tx_wait_release (struct rtw89_tx_wait_info *wait) +{ +dev_kfree_skb_any (wait->skb); +kfree_rcu (wait, rcu_head); +} + +static inline void rtw89_tx_wait_list_clear (struct rtw89_dev *rtwdev) +{ +struct rtw89_tx_wait_info *wait, *tmp; + +lockdep_assert_wiphy (rtwdev->hw->wiphy); + +list_for_each_entry_safe (wait, tmp, &rtwdev->tx_waits, list) { +if (!completion_done (&wait->completion)) +continue ; +list_del (&wait->list); +rtw89_tx_wait_release (wait); + } +} +static inline int rtw89_hci_tx_write (struct rtw89_dev *rtwdev,struct rtw89_core_tx_request *tx_req) {
@@ -6065,6 +6091,7 @@ static inline int rtw89_hci_tx_write(struct rtw89_dev *rtwdev,static inline void rtw89_hci_reset (struct rtw89_dev *rtwdev) { rtwdev->hci.ops->reset (rtwdev); +rtw89_tx_wait_list_clear (rtwdev); }static inline int rtw89_hci_start (struct rtw89_dev *rtwdev)
@@ -7122,11 +7149,12 @@ static inline struct sk_buff *rtw89_alloc_skb_for_rx(struct rtw89_dev *rtwdev,return dev_alloc_skb (length); } -static inline void rtw89_core_tx_wait_complete (struct rtw89_dev *rtwdev, +static inline bool rtw89_core_tx_wait_complete (struct rtw89_dev *rtwdev,struct rtw89_tx_skb_data *skb_data,bool tx_done) {struct rtw89_tx_wait_info *wait; +bool ret = false;rcu_read_lock ();
@@ -7134,11 +7162,14 @@ static inline void rtw89_core_tx_wait_complete(struct rtw89_dev *rtwdev,if (!wait) goto out; + ret = true; wait->tx_done = tx_done; -complete (&wait->completion); +/* Don't access skb anymore after completion */ +complete_all (&wait->completion); out:rcu_read_unlock (); +return ret; }static inline bool rtw89_is_mlo_1_1 (struct rtw89_dev *rtwdev)
drivers/net/wireless/realtek/rtw89/pci.c +2 -1
@@ -464,7 +464,8 @@ static void rtw89_pci_tx_status(struct rtw89_dev *rtwdev,struct rtw89_tx_skb_data *skb_data =RTW89_TX_SKB_CB (skb);struct ieee80211_tx_info *info; -rtw89_core_tx_wait_complete (rtwdev, skb_data, tx_status ==RTW89_TX_DONE ); +if (rtw89_core_tx_wait_complete (rtwdev, skb_data, tx_status ==RTW89_TX_DONE )) +return ; info =IEEE80211_SKB_CB (skb);ieee80211_tx_info_clear_status (info);
drivers/net/wireless/realtek/rtw89/ser.c +2 -0
@@ -501,7 +501,9 @@ static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt) }drv_stop_rx (ser); +wiphy_lock (wiphy);drv_trx_reset (ser); +wiphy_unlock (wiphy);/* wait m3 */ hal_send_m2_event (ser);
Signature · 1 line
2.51.0