UBUNTU-CVE-2026-89974: Use After Free

Published Sep 16, 2026
·
Updated

In the Linux kernel, the following vulnerability has been resolved: nvme-fc: fix double free of fabrics options when nvmeaddctrl() fails nvmfcreatectrl() owns the fabrics options and frees them whenever ->createctrl() returns an error, so a transport must not free them on its own error paths. nvme-fc tracks this by testing ctrl->ctrl.opts in nvmefcctrlfree(), which requires nvmefcinitctrl() to clear that pointer on every error exit. The coupling is implicit, and commit 1a9e218195a5 ("nvme: split device add from initialization") broke it by adding a second error exit. When nvmeaddctrl() fails, nvmefcinitctrl() jumps to outputctrl:, past the "ctrl->ctrl.opts = NULL" that only sits on the failctrl: path, so nvmefcctrlfree() frees the options and nvmfcreatectrl() frees them a second time: BUG: KASAN: slab-use-after-free in nvmffreeoptions+0x30/0x190 nvmffreeoptions+0x30/0x190 drivers/nvme/host/fabrics.c:1284 nvmfcreatectrl drivers/nvme/host/fabrics.c:1374 [inline] Freed by task 5534: nvmefcctrlfree drivers/nvme/host/fc.c:2374 [inline] nvmefcinitctrl+0xe17/0x1450 drivers/nvme/host/fc.c:3605 nvmeaddctrl() fails when devsetname() cannot allocate, so this is reachable under memory pressure or fault injection. Without KASAN the options are freed twice. Rather than clear the pointer on the second exit as well, derive ownership the way nvme-tcp, nvme-rdma and nvme-loop do, from list membership: their freectrl leaves the options alone unless the controller made it onto the transport list. The list cannot simply be populated on the success path as it is there. nvme-fc runs the initial connect synchronously via flushdelayedwork(), and the controller has to be reachable on rport->ctrllist for the whole of it: nvmefcunregisterremoteport() needs to find it to signal connectivity loss, nvmefcmatchdisconnls() matches an incoming Disconnect Association LS against ctrl->associationid, which is only assigned during that window, nvmefcresumecontroller() needs it on remoteport re-registration, and nvmefcexistingcontroller() uses it to reject a duplicate connect racing the one in flight. Keep the insertion where it is and add a failunlist: label, falling into failctrl:, for the error paths that run after it. The earlier error paths never reach the insertion and keep using failctrl: directly, so the list is only touched where the controller is actually on it. nvmefcctrlfree() cannot use the plain "goto freectrl" the other transports use, because it still has to putdevice(), release the rport reference and free the ida entry for resources taken before the insertion. Sample listempty() under rport->lock instead. ctrl->ctrl.opts also stays valid for the whole teardown now. That is not the bug being fixed, but it removes some fragility around the old idiom: nvmefreectrl() calls nvmeauthfree() before ->freectrl(), and ctrlmaxdhchaps() dereferences ctrl->opts without a NULL check when ctrl->dhchapctxs is set, which nvme-fc permits since NVMFALLOWEDOPTS allows the dhchap options. The nvme sysfs attributes that dereference ctrl->opts, such as hostnqn and address, evaluate their isvisible() test once at deviceadd() time and stay readable until cdevdevicedel().

Affected Software

128 affected componentsFixes available
ubuntu/linux-hwe-edge*
ubuntu/linux-aws-5.0*
ubuntu/linux-aws-5.3*
ubuntu/linux-azure*
ubuntu/linux-azure-5.3*
ubuntu/linux-azure-edge*
ubuntu/linux-gcp*
ubuntu/linux-gcp-5.3*
ubuntu/linux-gke-4.15*
ubuntu/linux-gke-5.4*
ubuntu/linux-gkeop-5.4*
ubuntu/linux-hwe*
ubuntu/linux-hwe-edge*
ubuntu/linux-oem*
ubuntu/linux-oracle-5.0*
ubuntu/linux-oracle-5.3*
ubuntu/linux-aws-5.11*
ubuntu/linux-aws-5.13*
ubuntu/linux-aws-5.8*
ubuntu/linux-azure-5.11*
ubuntu/linux-azure-5.13*
ubuntu/linux-azure-5.8*
ubuntu/linux-azure-fde*
ubuntu/linux-gcp-5.11*
ubuntu/linux-gcp-5.13*
ubuntu/linux-gcp-5.8*
ubuntu/linux-gke*
ubuntu/linux-gke-5.15*
ubuntu/linux-gkeop*
ubuntu/linux-gkeop-5.15*
ubuntu/linux-hwe-5.11*
ubuntu/linux-hwe-5.13*
ubuntu/linux-hwe-5.8*
ubuntu/linux-intel-5.13*
ubuntu/linux-oem-5.10*
ubuntu/linux-oem-5.13*
ubuntu/linux-oem-5.6*
ubuntu/linux-oracle-5.11*
ubuntu/linux-oracle-5.13*
ubuntu/linux-oracle-5.8*
ubuntu/linux-raspi2*
ubuntu/linux-riscv*
ubuntu/linux-riscv-5.11*
ubuntu/linux-riscv-5.8*
ubuntu/linux-allwinner-5.19*
ubuntu/linux-aws-5.19*
ubuntu/linux-aws-6.2*
ubuntu/linux-aws-6.5*
ubuntu/linux-azure-5.19*
ubuntu/linux-azure-6.2*
ubuntu/linux-azure-6.5*
ubuntu/linux-azure-fde-5.19*
ubuntu/linux-azure-fde-6.2*
ubuntu/linux-gcp-5.19*
ubuntu/linux-gcp-6.2*
ubuntu/linux-gcp-6.5*
ubuntu/linux-hwe-5.19*
ubuntu/linux-hwe-6.2*
ubuntu/linux-hwe-6.5*
ubuntu/linux-intel-iot-realtime*
ubuntu/linux-lowlatency-hwe-5.19*
ubuntu/linux-lowlatency-hwe-6.2*
ubuntu/linux-lowlatency-hwe-6.5*
ubuntu/linux-nvidia-6.2*
ubuntu/linux-nvidia-6.5*
ubuntu/linux-oem-5.17*
ubuntu/linux-oem-6.0*
ubuntu/linux-oem-6.1*
ubuntu/linux-oem-6.5*
ubuntu/linux-oracle-6.5*
ubuntu/linux-realtime*
ubuntu/linux-riscv*
ubuntu/linux-riscv-5.19*
ubuntu/linux-riscv-6.5*
ubuntu/linux-starfive-5.19*
ubuntu/linux-starfive-6.2*
ubuntu/linux-starfive-6.5*
ubuntu/linux-aws-6.14*
ubuntu/linux-aws-6.17*
ubuntu/linux-aws-7.0*
ubuntu/linux-azure-6.11*
ubuntu/linux-azure-6.14*
ubuntu/linux-azure-6.17*
ubuntu/linux-azure-7.0*
ubuntu/linux-azure-fde-6.14*
ubuntu/linux-azure-fde-6.17*
ubuntu/linux-azure-fde-7.0*
ubuntu/linux-azure-nvidia-6.14*
ubuntu/linux-gcp-6.11*
ubuntu/linux-gcp-6.14*
ubuntu/linux-gcp-7.0*
ubuntu/linux-hwe-6.11*
ubuntu/linux-hwe-6.14*
ubuntu/linux-hwe-6.17*
ubuntu/linux-hwe-7.0<7.0.0-42.42~24.04.1
7.0.0-42.42~24.04.1
ubuntu/linux-lowlatency-hwe-6.11*
ubuntu/linux-nvidia-6.11*
ubuntu/linux-nvidia-6.17*
ubuntu/linux-nvidia-7.0*
ubuntu/linux-oem-6.11*
ubuntu/linux-oem-6.14*
ubuntu/linux-oem-6.17*
ubuntu/linux-oem-6.8*
ubuntu/linux-oracle-6.14*
ubuntu/linux-oracle-6.17*
ubuntu/linux-oracle-7.0*
ubuntu/linux-raspi-realtime*
ubuntu/linux-realtime*
ubuntu/linux-riscv*
ubuntu/linux-riscv-6.14*
ubuntu/linux-riscv-6.17*
ubuntu/linux-riscv-7.0<7.0.0-42.42.1~24.04.1
7.0.0-42.42.1~24.04.1
ubuntu/linux-realtime-6.14*
ubuntu/linux-realtime-6.17*
ubuntu/linux<7.0.0-42.42
7.0.0-42.42
ubuntu/linux-aws<7.0.0-1016.17
7.0.0-1016.17
ubuntu/linux-azure*
ubuntu/linux-azure-fde*
ubuntu/linux-gcp<7.0.0-1016.18
7.0.0-1016.18
ubuntu/linux-gke*
ubuntu/linux-ibm<7.0.0-1017.17
7.0.0-1017.17
ubuntu/linux-nvidia*
ubuntu/linux-nvidia-bos*
ubuntu/linux-oem-7.0<7.0.0-1017.17
7.0.0-1017.17
ubuntu/linux-oracle<7.0.0-1015.16
7.0.0-1015.16
ubuntu/linux-raspi*
ubuntu/linux-realtime*
ubuntu/linux-riscv<7.0.0-42.42.1
7.0.0-42.42.1

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Compensating control

    In the Linux kernel nvme-fc transport, stop freeing fabrics options on transport error paths; add a fail_unlist label falling into fail_ctrl, and derive options ownership from controller list membership like nvme-tcp, nvme-rdma, and nvme-loop. Sample list_empty() under rport->lock before deciding whether to free the options.

Event History

Sep 16, 2026
CVE Published
via OSV·11:17 AM
Data Sourced
via OSV·11:17 AM
DescriptionSeverityAffected Software

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203