CVE-2026-43448

Published May 8, 2026

Last updated 5 months ago

Overview

Description
In the Linux kernel, the following vulnerability has been resolved: nvme-pci: Fix race bug in nvme_poll_irqdisable() In the following scenario, pdev can be disabled between (1) and (3) by (2). This sets pdev->msix_enabled = 0. Then, pci_irq_vector() will return MSI-X IRQ(>15) for (1) whereas return INTx IRQ(<=15) for (2). This causes IRQ warning because it tries to enable INTx IRQ that has never been disabled before. To fix this, save IRQ number into a local variable and ensure disable_irq() and enable_irq() operate on the same IRQ number. Even if pci_free_irq_vectors() frees the IRQ concurrently, disable_irq() and enable_irq() on a stale IRQ number is still valid and safe, and the depth accounting reamins balanced. task 1: nvme_poll_irqdisable() disable_irq(pci_irq_vector(pdev, nvmeq->cq_vector)) ...(1) enable_irq(pci_irq_vector(pdev, nvmeq->cq_vector)) ...(3) task 2: nvme_reset_work() nvme_dev_disable() pdev->msix_enable = 0; ...(2) crash log: ------------[ cut here ]------------ Unbalanced enable for IRQ 10 WARNING: kernel/irq/manage.c:753 at __enable_irq+0x102/0x190 kernel/irq/manage.c:753, CPU#1: kworker/1:0H/26 Modules linked in: CPU: 1 UID: 0 PID: 26 Comm: kworker/1:0H Not tainted 6.19.0-dirty #9 PREEMPT(voluntary) Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.3-0-ga6ed6b701f0a-prebuilt.qemu.org 04/01/2014 Workqueue: kblockd blk_mq_timeout_work RIP: 0010:__enable_irq+0x107/0x190 kernel/irq/manage.c:753 Code: ff df 48 89 fa 48 c1 ea 03 0f b6 14 02 48 89 f8 83 e0 07 83 c0 03 38 d0 7c 04 84 d2 75 79 48 8d 3d 2e 7a 3f 05 41 8b 74 24 2c <67> 48 0f b9 3a e8 ef b9 21 00 5b 41 5c 5d e9 46 54 66 03 e8 e1 b9 RSP: 0018:ffffc900001bf550 EFLAGS: 00010046 RAX: 0000000000000007 RBX: 0000000000000000 RCX: ffffffffb20c0e90 RDX: 0000000000000000 RSI: 000000000000000a RDI: ffffffffb74b88f0 RBP: ffffc900001bf560 R08: ffff88800197cf00 R09: 0000000000000001 R10: 0000000000000003 R11: 0000000000000003 R12: ffff8880012a6000 R13: 1ffff92000037eae R14: 000000000000000a R15: 0000000000000293 FS: 0000000000000000(0000) GS:ffff8880b49f7000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000555da4a25fa8 CR3: 00000000208e8000 CR4: 00000000000006f0 Call Trace: <TASK> enable_irq+0x121/0x1e0 kernel/irq/manage.c:797 nvme_poll_irqdisable+0x162/0x1c0 drivers/nvme/host/pci.c:1494 nvme_timeout+0x965/0x14b0 drivers/nvme/host/pci.c:1744 blk_mq_rq_timed_out block/blk-mq.c:1653 [inline] blk_mq_handle_expired+0x227/0x2d0 block/blk-mq.c:1721 bt_iter+0x2fc/0x3a0 block/blk-mq-tag.c:292 __sbitmap_for_each_set include/linux/sbitmap.h:269 [inline] sbitmap_for_each_set include/linux/sbitmap.h:290 [inline] bt_for_each block/blk-mq-tag.c:324 [inline] blk_mq_queue_tag_busy_iter+0x969/0x1e80 block/blk-mq-tag.c:536 blk_mq_timeout_work+0x627/0x870 block/blk-mq.c:1763 process_one_work+0x956/0x1aa0 kernel/workqueue.c:3257 process_scheduled_works kernel/workqueue.c:3340 [inline] worker_thread+0x65c/0xe60 kernel/workqueue.c:3421 kthread+0x41a/0x930 kernel/kthread.c:463 ret_from_fork+0x6f8/0x8c0 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246 </TASK> irq event stamp: 74478 hardirqs last enabled at (74477): [<ffffffffb5720a9c>] __raw_spin_unlock_irq include/linux/spinlock_api_smp.h:159 [inline] hardirqs last enabled at (74477): [<ffffffffb5720a9c>] _raw_spin_unlock_irq+0x2c/0x60 kernel/locking/spinlock.c:202 hardirqs last disabled at (74478): [<ffffffffb57207b5>] __raw_spin_lock_irqsave include/linux/spinlock_api_smp.h:108 [inline] hardirqs last disabled at (74478): [<ffffffffb57207b5>] _raw_spin_lock_irqsave+0x85/0xa0 kernel/locking/spinlock.c:162 softirqs last enabled at (74304): [<ffffffffb1e9466c>] __do_softirq kernel/softirq.c:656 [inline] softirqs last enabled at (74304): [<ffffffffb1e9466c>] invoke_softirq kernel/softirq.c:496 [inline] softirqs last enabled at (74304): [<ffffffffb1e9466c>] __irq_exit_rcu+0xdc/0x120 ---truncated---
Source
416baaa9-dc9f-4396-8d5f-8c081fb06d67
NVD status
Analyzed
Products
linux_kernel

Risk scores

CVSS 3.1

Type
Primary
Base score
4.7
Impact score
3.6
Exploitability score
1
Vector string
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:H
Severity
MEDIUM

Weaknesses

nvd@nist.gov
CWE-362

Social media

Hype score
Not currently trending

Configurations

  1. In the Linux kernel, the following vulnerability has been resolved: cgroup: Avoid iteration of dying tasks with zero refcount The commit 260fbcb92bbea ("cgroup: Move dying_tasks cleanup from cgroup_task_release() to cgroup_task_free()") extended the lifetime of tasks on the dying_tasks list. The iterators have provision to go through dying_tasks because of dying threadgroup leaders or explicit CSS_TASK_ITER_WITH_DEAD, however, it was expected that such tasks can obtain a new reference (that is possible before cgroup_task_release()/put_task_struct_rcu_user()). The tasks after cgroup_task_release() and before cgroup_task_free() are subject to race when they may or may not have ->usage count > 0. The race window is between css_task_iter_next() invocations when css_set_lock is released and we may arrive at a new ->task_pos. The iterator should not attempt to resurrect tasks whose ->usage count dropped to zero. (When that happens, __put_task_struct_rcu_cb() is already imminent and the returned task_struct would could be used after free.) As for the fix, we cannot simply check the signal->live count of a task on the dying list because that won't distinguish regular zombies waiting to be reaped from RCU remnant tasks that are going to be free'd. Therefore add an extra check to rule out ->usage==0 tasks from any iteration. The repeat: loop in css_task_iter_advance() doesn't consider ->usage count, so add a new loop to css_task_iter_next() to skip de-used tasks on the dying_list. Rough illustration of the possible race R (reader of cgroup.procs) T (thread) L (group leader) --------------------------------- -------------------------------- -------------------------------- L exits, signal->live > 0 cgroup_task_dead(L) css_set_skip_task_iters() // skips only cset->tasks list_add_tail(&L->cg_list, &cset->dying_tasks) css_task_iter_next() take css_set_lock css_task_iter_advance() leader && signal->live != 0 => it->task_pos = &L->cg_list release css_set_lock T exits --signal->live == 0 cgroup_task_dead(T) // css_set_lock release_task(T) cgroup_task_release(T) release_task(L) // zap_leader cgroup_task_release(L) put_task_struct_rcu_user(L) ...RCU... put_task_struct(L) L->usage = 0 /* L still on dying_tasks */ ...RCU... __put_task_struct(L) css_task_iter_next() // another iteration take css_set_lock it->task_pos = &L->cg_list get_task_struct(L) => addition on 0 drop css_set_lock cgroup_task_free(L) css_set_skip_task_iters() // dying skip comes too late free_task(L) cgroup_procs_show() task_pid_vnr(L)•CVE-2026-98163