From dea8e1c481c25a8dbc42f9a4975749ad81119ab4 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 6 Oct 2025 12:24:20 +0200 Subject: [PATCH] 6.12-stable patches added patches: media-i2c-tc358743-fix-use-after-free-bugs-caused-by-orphan-timer-in-probe.patch wifi-rtw89-fix-use-after-free-in-rtw89_core_tx_kick_off_and_wait.patch --- ...bugs-caused-by-orphan-timer-in-probe.patch | 149 ++++++++ queue-6.12/series | 2 + ...e-in-rtw89_core_tx_kick_off_and_wait.patch | 343 ++++++++++++++++++ 3 files changed, 494 insertions(+) create mode 100644 queue-6.12/media-i2c-tc358743-fix-use-after-free-bugs-caused-by-orphan-timer-in-probe.patch create mode 100644 queue-6.12/wifi-rtw89-fix-use-after-free-in-rtw89_core_tx_kick_off_and_wait.patch diff --git a/queue-6.12/media-i2c-tc358743-fix-use-after-free-bugs-caused-by-orphan-timer-in-probe.patch b/queue-6.12/media-i2c-tc358743-fix-use-after-free-bugs-caused-by-orphan-timer-in-probe.patch new file mode 100644 index 00000000000..0997b150e9e --- /dev/null +++ b/queue-6.12/media-i2c-tc358743-fix-use-after-free-bugs-caused-by-orphan-timer-in-probe.patch @@ -0,0 +1,149 @@ +From stable+bounces-183244-greg=kroah.com@vger.kernel.org Fri Oct 3 18:08:10 2025 +From: Sasha Levin +Date: Fri, 3 Oct 2025 12:06:02 -0400 +Subject: media: i2c: tc358743: Fix use-after-free bugs caused by orphan timer in probe +To: stable@vger.kernel.org +Cc: Duoming Zhou , Hans Verkuil , Sasha Levin +Message-ID: <20251003160602.3203886-1-sashal@kernel.org> + +From: Duoming Zhou + +[ Upstream commit 79d10f4f21a92e459b2276a77be62c59c1502c9d ] + +The state->timer is a cyclic timer that schedules work_i2c_poll and +delayed_work_enable_hotplug, while rearming itself. Using timer_delete() +fails to guarantee the timer isn't still running when destroyed, similarly +cancel_delayed_work() cannot ensure delayed_work_enable_hotplug has +terminated if already executing. During probe failure after timer +initialization, these may continue running as orphans and reference the +already-freed tc358743_state object through tc358743_irq_poll_timer. + +The following is the trace captured by KASAN. + +BUG: KASAN: slab-use-after-free in __run_timer_base.part.0+0x7d7/0x8c0 +Write of size 8 at addr ffff88800ded83c8 by task swapper/1/0 +... +Call Trace: + + dump_stack_lvl+0x55/0x70 + print_report+0xcf/0x610 + ? __pfx_sched_balance_find_src_group+0x10/0x10 + ? __run_timer_base.part.0+0x7d7/0x8c0 + kasan_report+0xb8/0xf0 + ? __run_timer_base.part.0+0x7d7/0x8c0 + __run_timer_base.part.0+0x7d7/0x8c0 + ? rcu_sched_clock_irq+0xb06/0x27d0 + ? __pfx___run_timer_base.part.0+0x10/0x10 + ? try_to_wake_up+0xb15/0x1960 + ? tmigr_update_events+0x280/0x740 + ? _raw_spin_lock_irq+0x80/0xe0 + ? __pfx__raw_spin_lock_irq+0x10/0x10 + tmigr_handle_remote_up+0x603/0x7e0 + ? __pfx_tmigr_handle_remote_up+0x10/0x10 + ? sched_balance_trigger+0x98/0x9f0 + ? sched_tick+0x221/0x5a0 + ? _raw_spin_lock_irq+0x80/0xe0 + ? __pfx__raw_spin_lock_irq+0x10/0x10 + ? tick_nohz_handler+0x339/0x440 + ? __pfx_tmigr_handle_remote_up+0x10/0x10 + __walk_groups.isra.0+0x42/0x150 + tmigr_handle_remote+0x1f4/0x2e0 + ? __pfx_tmigr_handle_remote+0x10/0x10 + ? ktime_get+0x60/0x140 + ? lapic_next_event+0x11/0x20 + ? clockevents_program_event+0x1d4/0x2a0 + ? hrtimer_interrupt+0x322/0x780 + handle_softirqs+0x16a/0x550 + irq_exit_rcu+0xaf/0xe0 + sysvec_apic_timer_interrupt+0x70/0x80 + +... + +Allocated by task 141: + kasan_save_stack+0x24/0x50 + kasan_save_track+0x14/0x30 + __kasan_kmalloc+0x7f/0x90 + __kmalloc_node_track_caller_noprof+0x198/0x430 + devm_kmalloc+0x7b/0x1e0 + tc358743_probe+0xb7/0x610 i2c_device_probe+0x51d/0x880 + really_probe+0x1ca/0x5c0 + __driver_probe_device+0x248/0x310 + driver_probe_device+0x44/0x120 + __device_attach_driver+0x174/0x220 + bus_for_each_drv+0x100/0x190 + __device_attach+0x206/0x370 + bus_probe_device+0x123/0x170 + device_add+0xd25/0x1470 + i2c_new_client_device+0x7a0/0xcd0 + do_one_initcall+0x89/0x300 + do_init_module+0x29d/0x7f0 + load_module+0x4f48/0x69e0 + init_module_from_file+0xe4/0x150 + idempotent_init_module+0x320/0x670 + __x64_sys_finit_module+0xbd/0x120 + do_syscall_64+0xac/0x280 + entry_SYSCALL_64_after_hwframe+0x77/0x7f + +Freed by task 141: + kasan_save_stack+0x24/0x50 + kasan_save_track+0x14/0x30 + kasan_save_free_info+0x3a/0x60 + __kasan_slab_free+0x3f/0x50 + kfree+0x137/0x370 + release_nodes+0xa4/0x100 + devres_release_group+0x1b2/0x380 + i2c_device_probe+0x694/0x880 + really_probe+0x1ca/0x5c0 + __driver_probe_device+0x248/0x310 + driver_probe_device+0x44/0x120 + __device_attach_driver+0x174/0x220 + bus_for_each_drv+0x100/0x190 + __device_attach+0x206/0x370 + bus_probe_device+0x123/0x170 + device_add+0xd25/0x1470 + i2c_new_client_device+0x7a0/0xcd0 + do_one_initcall+0x89/0x300 + do_init_module+0x29d/0x7f0 + load_module+0x4f48/0x69e0 + init_module_from_file+0xe4/0x150 + idempotent_init_module+0x320/0x670 + __x64_sys_finit_module+0xbd/0x120 + do_syscall_64+0xac/0x280 + entry_SYSCALL_64_after_hwframe+0x77/0x7f +... + +Replace timer_delete() with timer_delete_sync() and cancel_delayed_work() +with cancel_delayed_work_sync() to ensure proper termination of timer and +work items before resource cleanup. + +This bug was initially identified through static analysis. For reproduction +and testing, I created a functional emulation of the tc358743 device via a +kernel module and introduced faults through the debugfs interface. + +Fixes: 869f38ae07f7 ("media: i2c: tc358743: Fix crash in the probe error path when using polling") +Fixes: d32d98642de6 ("[media] Driver for Toshiba TC358743 HDMI to CSI-2 bridge") +Cc: stable@vger.kernel.org +Signed-off-by: Duoming Zhou +Signed-off-by: Hans Verkuil +[ replaced del_timer() instead of timer_delete() ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/media/i2c/tc358743.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/media/i2c/tc358743.c ++++ b/drivers/media/i2c/tc358743.c +@@ -2189,10 +2189,10 @@ static int tc358743_probe(struct i2c_cli + err_work_queues: + cec_unregister_adapter(state->cec_adap); + if (!state->i2c_client->irq) { +- del_timer(&state->timer); ++ timer_delete_sync(&state->timer); + flush_work(&state->work_i2c_poll); + } +- cancel_delayed_work(&state->delayed_work_enable_hotplug); ++ cancel_delayed_work_sync(&state->delayed_work_enable_hotplug); + mutex_destroy(&state->confctl_mutex); + err_hdl: + media_entity_cleanup(&sd->entity); diff --git a/queue-6.12/series b/queue-6.12/series index e69de29bb2d..524680a779d 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -0,0 +1,2 @@ +wifi-rtw89-fix-use-after-free-in-rtw89_core_tx_kick_off_and_wait.patch +media-i2c-tc358743-fix-use-after-free-bugs-caused-by-orphan-timer-in-probe.patch diff --git a/queue-6.12/wifi-rtw89-fix-use-after-free-in-rtw89_core_tx_kick_off_and_wait.patch b/queue-6.12/wifi-rtw89-fix-use-after-free-in-rtw89_core_tx_kick_off_and_wait.patch new file mode 100644 index 00000000000..126c89b5c00 --- /dev/null +++ b/queue-6.12/wifi-rtw89-fix-use-after-free-in-rtw89_core_tx_kick_off_and_wait.patch @@ -0,0 +1,343 @@ +From stable+bounces-183316-greg=kroah.com@vger.kernel.org Fri Oct 3 20:47:55 2025 +From: Sasha Levin +Date: Fri, 3 Oct 2025 14:47:46 -0400 +Subject: wifi: rtw89: fix use-after-free in rtw89_core_tx_kick_off_and_wait() +To: stable@vger.kernel.org +Cc: Fedor Pchelkin , Zong-Zhe Yang , Ping-Ke Shih , Sasha Levin +Message-ID: <20251003184746.3288963-1-sashal@kernel.org> + +From: Fedor Pchelkin + +[ 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). + +Fixes: 1ae5ca615285 ("wifi: rtw89: add function to wait for completion of TX skbs") +Cc: stable@vger.kernel.org +Suggested-by: Zong-Zhe Yang +Signed-off-by: Fedor Pchelkin +Acked-by: Ping-Ke Shih +Signed-off-by: Ping-Ke Shih +Link: https://patch.msgid.link/20250919210852.823912-2-pchelkin@ispras.ru +[ added wiphy variable declarations ] +Signed-off-by: Sasha Levin +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/realtek/rtw89/core.c | 31 +++++++++++++++++++++----- + drivers/net/wireless/realtek/rtw89/core.h | 35 ++++++++++++++++++++++++++++-- + drivers/net/wireless/realtek/rtw89/pci.c | 3 +- + drivers/net/wireless/realtek/rtw89/ser.c | 3 ++ + 4 files changed, 63 insertions(+), 9 deletions(-) + +--- a/drivers/net/wireless/realtek/rtw89/core.c ++++ b/drivers/net/wireless/realtek/rtw89/core.c +@@ -960,6 +960,14 @@ rtw89_core_tx_update_desc_info(struct rt + } + } + ++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; +@@ -977,6 +985,8 @@ int rtw89_core_tx_kick_off_and_wait(stru + unsigned 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); +@@ -984,18 +994,23 @@ int rtw89_core_tx_kick_off_and_wait(stru + } + + 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; + } +@@ -4419,6 +4434,7 @@ int rtw89_core_start(struct rtw89_dev *r + void rtw89_core_stop(struct rtw89_dev *rtwdev) + { + struct rtw89_btc *btc = &rtwdev->btc; ++ struct wiphy *wiphy = rtwdev->hw->wiphy; + + /* Prvent to stop twice; enter_ips and ops_stop */ + if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags)) +@@ -4437,6 +4453,7 @@ void rtw89_core_stop(struct rtw89_dev *r + cancel_work_sync(&btc->dhcp_notify_work); + cancel_work_sync(&btc->icmp_notify_work); + cancel_delayed_work_sync(&rtwdev->txq_reinvoke_work); ++ wiphy_delayed_work_cancel(wiphy, &rtwdev->tx_wait_work); + cancel_delayed_work_sync(&rtwdev->track_work); + cancel_delayed_work_sync(&rtwdev->chanctx_work); + cancel_delayed_work_sync(&rtwdev->coex_act1_work); +@@ -4657,6 +4674,7 @@ int rtw89_core_init(struct rtw89_dev *rt + continue; + INIT_LIST_HEAD(&rtwdev->scan_info.pkt_list[band]); + } ++ 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); +@@ -4666,6 +4684,7 @@ int rtw89_core_init(struct rtw89_dev *rt + INIT_DELAYED_WORK(&rtwdev->coex_bt_devinfo_work, rtw89_coex_bt_devinfo_work); + INIT_DELAYED_WORK(&rtwdev->coex_rfk_chk_work, rtw89_coex_rfk_chk_work); + INIT_DELAYED_WORK(&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work); ++ wiphy_delayed_work_init(&rtwdev->tx_wait_work, rtw89_tx_wait_work); + INIT_DELAYED_WORK(&rtwdev->forbid_ba_work, rtw89_forbid_ba_work); + INIT_DELAYED_WORK(&rtwdev->antdiv_work, rtw89_phy_antdiv_work); + rtwdev->txq_wq = alloc_workqueue("rtw89_tx_wq", WQ_UNBOUND | WQ_HIGHPRI, 0); +--- a/drivers/net/wireless/realtek/rtw89/core.h ++++ b/drivers/net/wireless/realtek/rtw89/core.h +@@ -3406,9 +3406,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; + }; + +@@ -5539,6 +5542,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; +@@ -5735,6 +5741,26 @@ u8 rtw89_sta_link_inst_get_index(struct + return rtwsta_link - rtwsta->links_inst; + } + ++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) + { +@@ -5744,6 +5770,7 @@ static inline int rtw89_hci_tx_write(str + 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) +@@ -6745,11 +6772,12 @@ static inline struct sk_buff *rtw89_allo + 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(); + +@@ -6757,11 +6785,14 @@ static inline void rtw89_core_tx_wait_co + 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) +--- a/drivers/net/wireless/realtek/rtw89/pci.c ++++ b/drivers/net/wireless/realtek/rtw89/pci.c +@@ -458,7 +458,8 @@ static void rtw89_pci_tx_status(struct r + 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); +--- a/drivers/net/wireless/realtek/rtw89/ser.c ++++ b/drivers/net/wireless/realtek/rtw89/ser.c +@@ -484,6 +484,7 @@ static void ser_l1_reset_pre_st_hdl(stru + static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt) + { + struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser); ++ struct wiphy *wiphy = rtwdev->hw->wiphy; + + switch (evt) { + case SER_EV_STATE_IN: +@@ -496,7 +497,9 @@ static void ser_reset_trx_st_hdl(struct + } + + drv_stop_rx(ser); ++ wiphy_lock(wiphy); + drv_trx_reset(ser); ++ wiphy_unlock(wiphy); + + /* wait m3 */ + hal_send_m2_event(ser); -- 2.47.3