In the Linux kernel, the following vulnerability has been resolved:
hwrng: core - use RCU and work_struct to fix race condition
Currently, hwrngfill is not cleared until the hwrngfillfn() thread exits. Since hwrngunregister() reads hwrngfill outside the rngmutex lock, a concurrent hwrngunregister() may call kthread_stop() again on the same task.
Additionally, if hwrngunregister() is called immediately after hwrngregister(), the stopped thread may have never been executed. Thus, hwrngfill remains dirty even after hwrngunregister() returns. In this case, subsequent calls to hwrngregister() will fail to start new threads, and hwrngunregister() will call kthread_stop() on the same freed task. In both cases, a use-after-free occurs:
refcountt: addition on 0; use-after-free. WARNING: ... at lib/refcount.c:25 refcountwarnsaturate+0xec/0x1c0 Call Trace: kthreadstop+0x181/0x360 hwrngunregister+0x288/0x380 virtrngremove+0xe3/0x200
This patch fixes the race by protecting the global hwrngfill pointer inside the rngmutex lock, so that hwrngfillfn() thread is stopped only once, and calls to kthreadrun() and kthread_stop() are serialized with the lock held.
To avoid deadlock in hwrngfillfn() while being stopped with the lock held, we convert currentrng to RCU, so that getcurrentrng() can read currentrng without holding the lock. To remove the lock from putrng(), we also delay the actual cleanup into a work_struct.
Since getcurrentrng() no longer returns ERRPTR values, the ISERR() checks are removed from its callers.
With hwrngfill protected by the rngmutex lock, hwrngfillfn() can no longer clear hwrngfill itself. Therefore, if hwrngfillfn() returns directly after currentrng is dropped, kthreadstop() would be called on a freed taskstruct later. To fix this, hwrngfillfn() calls schedule() now to keep the task alive until being stopped. The kthreadstop() call is also moved from hwrngunregister() to dropcurrentrng(), ensuring kthreadstop() is called on all possible paths where current_rng becomes NULL, so that the thread would not wait forever.
{
"osv_generated_from": "https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/45xxx/CVE-2026-45949.json",
"cna_assigner": "Linux"
}