CVE-2026-98832: blk-cgroup: save IRQ state in blkg_tryget_closest()
In the Linux kernel, the following vulnerability has been resolved:
blk-cgroup: save IRQ state in blkgtrygetclosest()
biosetdev() associates the bio with a blkg through bioassociateblkg(). If the blkg lookup misses, blkgtrygetclosest() takes q->queuelock with spinlockirq() and releases it with spinunlockirq(), which unconditionally enables local interrupts.
Callers may call biosetdev() with interrupts already disabled, e.g. dm-thin's poolmap() does so while holding pool->lock taken with spinlockirq(). The nested spinunlockirq() then enables interrupts while pool->lock is still held, so an I/O completion softirq can run on the same CPU, re-acquire pool->lock (thinendio(), or overwriteendio() -> completemappingpreparation()) and deadlock. lockdep reports this as inconsistent SOFTIRQ-ON-W to IN-SOFTIRQ-W usage.
Commit 3a762de55b4e ("block: save irq state in blkglookupcreate()") fixed the same problem while the lock lived in blkglookupcreate(), but commit 9327a865e395 ("blk-cgroup: don't nest queuelock under rcu in blkglookupcreate()") moved the locking into blkgtrygetclosest() and reverted it to spinlockirq().
Save and restore the caller's IRQ state instead.
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Compensating control
In blk-cgroup's blkg_tryget_closest(), save and restore the caller's IRQ state instead of unconditionally using spin_lock_irq() and spin_unlock_irq().
Event History
Frequently Asked Questions
Which systems or I/O paths are most likely to encounter this issue?
The described trigger involves callers that invoke bio_set_dev() with local interrupts already disabled while holding a spinlock. The report specifically identifies dm-thin's pool_map() while pool->lock is held with spin_lock_irq(), combined with a blk-cgroup lookup miss.
What is the operational impact if the issue is triggered?
Local interrupts can be enabled while pool->lock remains held. An I/O completion softirq may then run on the same CPU, attempt to acquire that lock through thin_endio() or overwrite_endio(), and deadlock.
How can I check whether the fix is present?
Inspect the kernel's blkg_tryget_closest() implementation to confirm that it saves and restores the caller's IRQ state when taking q->queue_lock, rather than using spin_lock_irq() and spin_unlock_irq() in a way that unconditionally enables interrupts.