CVE-2026-46187
Description
In the Linux kernel, the following vulnerability has been resolved:
wifi: rsi: fix kthread lifetime race between self-exit and external-stop
RSI driver use both self-exit(kthread_complete_and_exit) and external-stop (kthread_stop) when killing a kthread. Generally, kthread_stop() is called first, and in this case, no particular issues occur.
However, in rare instances where kthread_complete_and_exit() is called first and then kthread_stop() is called, a UAF occurs because the kthread object, which has already exited and been freed, is accessed again.
Therefore, to prevent this with minimal modification, you must remove kthread_stop() and change the code to wait until the self-exit operation is completed.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A race condition in the Linux kernel's rsi Wi-Fi driver can cause use-after-free when a kthread exits itself before an external stop request is processed.
Vulnerability
A race condition exists in the Linux kernel's rsi Wi-Fi driver (drivers/net/wireless/rsi/) where a kthread's lifetime can end via two paths: self-exit using kthread_complete_and_exit() or external-stop using kthread_stop(). In a rare race window, if the kthread completes and frees itself before kthread_stop() is invoked, the subsequent call to kthread_stop() accesses the already-freed kthread object, leading to a use-after-free (UAF) vulnerability. The issue is present in versions prior to the fix commit 4f4c9b13c485 [1].
Exploitation
An attacker would need no special privileges; the race condition occurs during normal driver operation when a kthread exits unusually quickly after being started. The root cause is a flaw in the driver's kthread management logic, not requiring user interaction beyond system operation. The time window is narrow and depends on scheduling, but the scenario is triggerable by an attacker who can influence driver reinitialization or Power Saving (PS) mode transitions.
Impact
Successful exploitation of the UAF condition can lead to memory corruption, potentially allowing an attacker to escalate privileges or cause a denial of service (system crash). The driver runs in kernel context, so the impact includes arbitrary code execution at kernel privilege level, resulting in full compromise of the system's confidentiality, integrity, and availability. The specific outcome depends on the state of the freed memory and what data structures are overwritten.
Mitigation
The fix was committed to the mainline Linux kernel as commit 4f4c9b13c485abd0a2d2c97f9db339d1dd8e147f [1]. The change replaces the mixed use of kthread_stop() with a wait for the kthread to self-exit, preventing the race. Users should update to a kernel version containing this commit or apply the patch if using an affected version. No workaround other than patching is documented; there is no indication of the CVE being listed on CISA's KEV.
AI Insight generated on May 28, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1Patches
104f9a4ae8d2c1wifi: rsi: fix kthread lifetime race between self-exit and external-stop
2 files changed · +4 −8
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 7aa5124575cfef..c40f8101febcb8 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 7aa5124575cfef..c40f8101febcb8 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
95fcb436586dwifi: rsi: fix kthread lifetime race between self-exit and external-stop
2 files changed · +4 −8
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 7aa5124575cfef..c40f8101febcb8 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 7aa5124575cfef..c40f8101febcb8 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
16d9f674c619wifi: rsi: fix kthread lifetime race between self-exit and external-stop
2 files changed · +4 −8
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 7aa5124575cfef..c40f8101febcb8 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 7aa5124575cfef..c40f8101febcb8 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
4f4c9b13c485wifi: rsi: fix kthread lifetime race between self-exit and external-stop
2 files changed · +4 −8
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 7aa5124575cfef..c40f8101febcb8 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 7aa5124575cfef..c40f8101febcb8 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
db57a1aa54ffwifi: rsi: fix kthread lifetime race between self-exit and external-stop
2 files changed · +4 −8
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 591602beeec689..3cdf9ded876d9c 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 591602beeec689..3cdf9ded876d9c 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
db57a1aa54ffwifi: rsi: fix kthread lifetime race between self-exit and external-stop
2 files changed · +4 −8
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 591602beeec689..3cdf9ded876d9c 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 591602beeec689..3cdf9ded876d9c 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
16d9f674c619wifi: rsi: fix kthread lifetime race between self-exit and external-stop
2 files changed · +4 −8
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 7aa5124575cfef..c40f8101febcb8 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 7aa5124575cfef..c40f8101febcb8 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
4f4c9b13c485wifi: rsi: fix kthread lifetime race between self-exit and external-stop
2 files changed · +4 −8
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 7aa5124575cfef..c40f8101febcb8 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 7aa5124575cfef..c40f8101febcb8 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
4f9a4ae8d2c1wifi: rsi: fix kthread lifetime race between self-exit and external-stop
2 files changed · +4 −8
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 7aa5124575cfef..c40f8101febcb8 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 7aa5124575cfef..c40f8101febcb8 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
95fcb436586dwifi: rsi: fix kthread lifetime race between self-exit and external-stop
2 files changed · +4 −8
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 7aa5124575cfef..c40f8101febcb8 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
drivers/net/wireless/rsi/rsi_common.h+2 −4 modifieddiff --git a/drivers/net/wireless/rsi/rsi_common.h b/drivers/net/wireless/rsi/rsi_common.h index 7aa5124575cfef..c40f8101febcb8 100644 --- a/drivers/net/wireless/rsi/rsi_common.h +++ b/drivers/net/wireless/rsi/rsi_common.h @@ -70,12 +70,11 @@ static inline int rsi_create_kthread(struct rsi_common *common, return 0; } -static inline int rsi_kill_thread(struct rsi_thread *handle) +static inline void rsi_kill_thread(struct rsi_thread *handle) { atomic_inc(&handle->thread_done); rsi_set_event(&handle->event); - - return kthread_stop(handle->task); + wait_for_completion(&handle->completion); } void rsi_mac80211_detach(struct rsi_hw *hw); -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Race condition in the RSI wireless driver where a kthread can be freed (via self-exit with kthread_complete_and_exit) before a subsequent kthread_stop() call completes, causing a use-after-free."
Attack vector
The RSI driver's rsi_kill_thread() function previously called kthread_stop() after signaling the thread to exit. In a rare race, the kernel thread's own kthread_complete_and_exit() path runs first, freeing the kthread object. When kthread_stop() then accesses the already-freed kthread structure, a use-after-free (UAF) occurs [patch_id=2897938]. No special network or authentication preconditions are required; the race is triggered during normal driver teardown when a kthread exits on its own just as the driver calls rsi_kill_thread().
Affected code
The vulnerable code is in drivers/net/wireless/rsi/rsi_common.h, in the inline function rsi_kill_thread() [patch_id=2897938]. The function previously called kthread_stop(handle->task) after setting the thread_done flag and signaling the event.
What the fix does
The patch in rsi_common.h replaces the kthread_stop(handle->task) call with wait_for_completion(&handle->completion) [patch_id=2897938]. This ensures that rsi_kill_thread() waits for the kthread to finish its self-exit via kthread_complete_and_exit() rather than trying to externally stop it. The return type is also changed from int to void since the function no longer returns kthread_stop's result. This eliminates the race by removing the external-stop path entirely, relying solely on the self-exit mechanism.
Preconditions
- configThe RSI wireless driver must be loaded and managing a kernel thread that can self-exit via kthread_complete_and_exit().
- inputThe race window is triggered during driver teardown when rsi_kill_thread() is called concurrently with the kthread's own self-exit.
Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- git.kernel.org/stable/c/16d9f674c619838bdeae42abc0929c9c5477ea1fnvd
- git.kernel.org/stable/c/4f4c9b13c485abd0a2d2c97f9db339d1dd8e147fnvd
- git.kernel.org/stable/c/4f9a4ae8d2c198f01611ea376034c326ef43ab56nvd
- git.kernel.org/stable/c/95fcb436586dc3c2983537d557ac05bbc6a027f3nvd
- git.kernel.org/stable/c/db57a1aa54ff68669781976e4edb045e09e2b65bnvd
News mentions
0No linked articles in our index yet.