CVE-2026-90325 in Linux
Summary
by MITRE • 09/17/2026
In the Linux kernel, the following vulnerability has been resolved:
blk-cgroup: skip dying blkg in blkcg_activate_policy()
When switching IO schedulers on a block device, blkcg_activate_policy() can race with concurrent blkcg deletion, leading to a use-after-free in rcu_accelerate_cbs.
T1: T2: blkg_destroy kill(&blkg->refcnt) // blkg->refcnt=1->0 blkg_release // call_rcu(__blkg_release) ... blkg_free_workfn ->pd_free_fn(pd) elv_iosched_store elevator_switch ... iterate blkg list blkg_get(blkg) // blkg->refcnt=0->1 list_del_init(&blkg->q_node) blkg_put(pinned_blkg) // blkg->refcnt=1->0 blkg_release // call_rcu again rcu_accelerate_cbs // uaf
Fix this by checking hlist_unhashed(&blkg->blkcg_node) before getting a reference to the blkg. This is the same check used in blkg_destroy() to detect if a blkg has already been destroyed. If the blkg is already unhashed, skip processing it since it's being destroyed.
If you want to get the best quality for vulnerability data then you always have to consider VulDB.
Analysis
by VulDB Data Team • 09/18/2026
The Linux kernel block layer contains a critical race condition within the blk-cgroup subsystem that can lead to a use-after-free vulnerability during IO scheduler transitions. This flaw specifically manifests in the blkcg_activate_policy function when an administrator attempts to switch the IO scheduler on a block device while concurrent cgroup deletion operations are occurring. The root cause lies in the lack of synchronization between policy activation and the lifecycle management of block group structures, known as blkg objects. When these two processes race against each other, the kernel may attempt to access or manipulate memory that has already been freed by another thread, resulting in undefined behavior, potential system crashes, or arbitrary code execution if an attacker can influence the timing of these events.
The technical mechanism of this vulnerability involves a specific sequence of reference counting and RCU (Read-Copy-Update) operations. In one scenario, a blkg object is targeted for destruction by another thread which decrements its reference count to zero and schedules it for deferred freeing via call_rcu. Simultaneously, the IO scheduler switch operation iterates through the list of block groups associated with a queue. It attempts to acquire a reference to each blkg using blkg_get, which increments the reference count from one back to one after it had been set to zero by the destruction thread. This action effectively resurrects an object that is in the process of being freed. Subsequently, when the scheduler code releases its pinned reference via blkg_put, the reference count drops back to zero, triggering another call_rcu invocation for the same memory region. The final stage involves rcu_accelerate_cbs attempting to access this now-freed memory, constituting a classic use-after-free condition where the kernel operates on stale pointers that may have been reallocated for other purposes.
This vulnerability aligns with CWE-416, Use After Free, as it involves accessing memory after it has been released back to the system without proper validation of its current state or ownership. From an offensive security perspective, this flaw maps to ATT&CK technique T1203, Exploitation for Client Execution, if leveraged in a local privilege escalation context where a user with access to block devices can trigger the race condition to gain kernel-level code execution. The impact of such a vulnerability is severe, as it compromises the integrity and availability of the operating system. A successful exploitation could lead to kernel panics causing denial of service or allow an unprivileged user to escalate privileges by executing arbitrary code within the kernel space, thereby bypassing all standard security controls and gaining full control over the host system.
The resolution for this issue involves a precise check performed before acquiring a reference to any blkg object during policy activation. The fix introduces a verification step that checks whether the blkg is unhashed from its associated cgroup list using hlist_unhashed(&blkg->blkcg_node). This flag serves as an indicator that the block group has already been marked for destruction and removed from active processing lists by another thread. By skipping any blkg object identified as unhashed, the kernel prevents the resurrection of dying objects and ensures that only valid, live structures are processed during IO scheduler switches. This approach mirrors the logic used in blkg_destroy to detect completion status, thereby maintaining consistency across the subsystem's lifecycle management.
To mitigate this vulnerability, system administrators should ensure that their Linux kernels are updated with patches addressing this specific race condition in the blk-cgroup module. In environments where dynamic switching of IO schedulers is required, it is advisable to minimize concurrent cgroup modifications during such transitions or schedule these administrative tasks during maintenance windows when active I/O operations and group changes can be controlled more strictly. Additionally, enabling kernel hardening features such as KASAN (Kernel Address Sanitizer) in development builds can help detect similar race conditions early in the software lifecycle before they reach production systems. Regular auditing of block device configurations and adherence to least-privilege principles for users with access to /sys/block interfaces further reduces the attack surface available for exploiting this timing-dependent flaw.