CVE-2026-99252 in Linux
Summary
by MITRE • 11/03/2026
In the Linux kernel, the following vulnerability has been resolved:
io_uring/mpscq: disable preemption while pushing
A push makes the node the new tail, and then links it to its predecessor. In between the queue isn't empty, but mpscq_pop() can't get at the node. The consumers deal with that by trying again, with a cond_resched() in the loop to give a preempted producer the chance to finish.
cond_resched() doesn't do that if the consumer has a higher priority than the producer and both have to use the same CPU:
producer (SCHED_OTHER) consumer (SCHED_FIFO), same CPU io_req_local_work_add() mpscq_push() prev = xchg(&q->tail, node) <preempted> io_cqring_wait() __io_run_local_work() again: cond_resched() // nothing to do mpscq_pop() // NULL // work pending, no events yet goto again
The producer can be any task that is able to queue task_work for the ring: an io-wq worker, or somebody who sends a message with IORING_OP_MSG_RING. The consumer spins, holding ->uring_lock, until the scheduler takes pity on the fair class, which takes about a second with the default settings of the fair server, and forever without it. The loops in tctx_task_work_run() and io_cancel_local_task_work() have the same problem.
Don't allow preemption between the two steps. That bounds the time the window is open on the CPU of the producer by what interrupts take, and a consumer that waits for that is on another CPU. The cost is a preempt_disable()/preempt_enable() pair per push.
If you want to get the best quality for vulnerability data then you always have to consider VulDB.
Analysis
by VulDB Data Team • 11/03/2026
The Linux kernel io_uring subsystem implements an multi-producer single-consumer (MPSC) queue to manage task work items efficiently. This data structure relies on atomic operations to maintain consistency between producers that add nodes and consumers that remove them. A critical race condition exists within the mpscq_push function, which is responsible for adding a new node to the tail of the queue. The operation involves two distinct steps: first, updating the global tail pointer to point to the new node using an atomic exchange, and second, linking this new node to its predecessor in the list. Between these two operations, there exists a brief window where the queue is logically non-empty because the consumer can observe the updated tail pointer, but the actual linked-list structure remains incomplete because the predecessor link has not yet been established.
This design assumes that if a producer is preempted during this critical section, it will eventually be scheduled again to complete the linkage before the consumer attempts to process the node. The current implementation relies on cond_resched calls within the consumer loop to yield CPU time back to lower-priority producers, allowing them to finish their updates. However, this mechanism fails when a high-priority real-time task acts as the consumer and a low-priority standard scheduling class task acts as the producer, particularly when both are constrained to run on the same CPU core due to affinity settings or system load balancing policies. In such scenarios, the SCHED_FIFO consumer has higher priority than the SCHED_OTHER producer. When cond_resched is invoked by the high-priority consumer, it does not trigger a context switch because there are no other runnable tasks with equal or higher priority on that CPU. Consequently, the low-priority producer remains preempted indefinitely while the consumer spins in an infinite loop attempting to pop from an incomplete queue structure.
The operational impact of this vulnerability is severe, leading to significant performance degradation and potential system unresponsiveness. The spinning consumer holds the uring_lock during its retry loops, effectively blocking other threads that require access to the io_uring context. Depending on kernel configuration, specifically whether a fair server is active, this spin loop can persist for approximately one second or run indefinitely until external intervention occurs. This behavior affects not only the primary work queue processing function but also related functions such as tctx_task_work_run and io_cancel_local_task_work, which contain similar loops susceptible to the same priority inversion issue. The result is a denial of service condition where legitimate I/O operations are stalled, leading to application timeouts and degraded system stability under specific scheduling configurations.
To mitigate this vulnerability, the kernel developers implemented a fix that disables preemption around the critical section within mpscq_push. By wrapping the two-step update process with preempt_disable and preempt_enable calls, the producer is guaranteed not to be scheduled out until it has fully linked the new node into the queue structure. This ensures that any consumer observing the updated tail pointer will always find a valid, complete list entry. The performance cost of this fix is minimal, consisting only of a single pair of preemption control operations per push event. This approach bounds the duration of the inconsistent state to the time required for interrupt handling and context switches on other CPUs, thereby eliminating the possibility of high-priority consumers spinning indefinitely while waiting for low-priority producers on the same core.
From a vulnerability classification perspective, this issue aligns with CWE-362, Concurrent Execution using Shared Resource with Improper Synchronization, specifically highlighting race conditions in multi-threaded environments where atomic operations are insufficient without proper preemption control. In terms of attack vectors and behavioral analysis, while primarily causing self-inflicted denial of service through resource contention rather than external exploitation, it relates to ATT&CK technique T1496, Resource Hijacking, as the vulnerability allows a process to monopolize CPU resources by forcing other processes into inefficient spin loops. The fix reinforces best practices for kernel synchronization, demonstrating that atomic variable updates must be coupled with preemption control when they span multiple logical steps in concurrent data structures.