{"id":24661,"date":"2026-09-16T20:32:36","date_gmt":"2026-09-16T12:32:36","guid":{"rendered":"https:\/\/www.54iter.cn\/?vuln=cve-2026-89974"},"modified":"2026-09-16T20:32:36","modified_gmt":"2026-09-16T12:32:36","slug":"cve-2026-89974","status":"publish","type":"vuln","link":"https:\/\/www.54iter.cn\/?vuln=cve-2026-89974","title":{"rendered":"CVE-2026-89974 &#8211; In the Linux kernel, the following vulnerability has been resolved:\n\nnvme-fc: fix double f"},"content":{"rendered":"<p>In the Linux kernel, the following vulnerability has been resolved:<\/p>\n<p>nvme-fc: fix double free of fabrics options when nvme_add_ctrl() fails<\/p>\n<p>nvmf_create_ctrl() owns the fabrics options and frees them whenever<br \/>\n-&gt;create_ctrl() returns an error, so a transport must not free them on<br \/>\nits own error paths.  nvme-fc tracks this by testing ctrl-&gt;ctrl.opts in<br \/>\nnvme_fc_ctrl_free(), which requires nvme_fc_init_ctrl() to clear that<br \/>\npointer on every error exit.<\/p>\n<p>The coupling is implicit, and commit 1a9e218195a5 (&#8220;nvme: split device<br \/>\nadd from initialization&#8221;) broke it by adding a second error exit.  When<br \/>\nnvme_add_ctrl() fails, nvme_fc_init_ctrl() jumps to out_put_ctrl:, past<br \/>\nthe &#8220;ctrl-&gt;ctrl.opts = NULL&#8221; that only sits on the fail_ctrl: path, so<br \/>\nnvme_fc_ctrl_free() frees the options and nvmf_create_ctrl() frees them<br \/>\na second time:<\/p>\n<p>  BUG: KASAN: slab-use-after-free in nvmf_free_options+0x30\/0x190<br \/>\n   nvmf_free_options+0x30\/0x190 drivers\/nvme\/host\/fabrics.c:1284<br \/>\n   nvmf_create_ctrl drivers\/nvme\/host\/fabrics.c:1374 [inline]<br \/>\n  Freed by task 5534:<br \/>\n   nvme_fc_ctrl_free drivers\/nvme\/host\/fc.c:2374 [inline]<br \/>\n   nvme_fc_init_ctrl+0xe17\/0x1450 drivers\/nvme\/host\/fc.c:3605<\/p>\n<p>nvme_add_ctrl() fails when dev_set_name() cannot allocate, so this is<br \/>\nreachable under memory pressure or fault injection.  Without KASAN the<br \/>\noptions are freed twice.<\/p>\n<p>Rather than clear the pointer on the second exit as well, derive<br \/>\nownership the way nvme-tcp, nvme-rdma and nvme-loop do, from list<br \/>\nmembership: their free_ctrl leaves the options alone unless the<br \/>\ncontroller made it onto the transport list.<\/p>\n<p>The list cannot simply be populated on the success path as it is there.<br \/>\nnvme-fc runs the initial connect synchronously via flush_delayed_work(),<br \/>\nand the controller has to be reachable on rport-&gt;ctrl_list for the whole<br \/>\nof it: nvme_fc_unregister_remoteport() needs to find it to signal<br \/>\nconnectivity loss, nvme_fc_match_disconn_ls() matches an incoming<br \/>\nDisconnect Association LS against ctrl-&gt;association_id, which is only<br \/>\nassigned during that window, nvme_fc_resume_controller() needs it on<br \/>\nremoteport re-registration, and nvme_fc_existing_controller() uses it to<br \/>\nreject a duplicate connect racing the one in flight.<\/p>\n<p>Keep the insertion where it is and add a fail_unlist: label, falling<br \/>\ninto fail_ctrl:, for the error paths that run after it.  The earlier<br \/>\nerror paths never reach the insertion and keep using fail_ctrl:<br \/>\ndirectly, so the list is only touched where the controller is actually<br \/>\non it.<\/p>\n<p>nvme_fc_ctrl_free() cannot use the plain &#8220;goto free_ctrl&#8221; the other<br \/>\ntransports use, because it still has to put_device(), release the rport<br \/>\nreference and free the ida entry for resources taken before the<br \/>\ninsertion.  Sample list_empty() under rport-&gt;lock instead.<\/p>\n<p>ctrl-&gt;ctrl.opts also stays valid for the whole teardown now.  That is<br \/>\nnot the bug being fixed, but it removes some fragility around the old<br \/>\nidiom: nvme_free_ctrl() calls nvme_auth_free() before -&gt;free_ctrl(), and<br \/>\nctrl_max_dhchaps() dereferences ctrl-&gt;opts without a NULL check when<br \/>\nctrl-&gt;dhchap_ctxs is set, which nvme-fc permits since NVMF_ALLOWED_OPTS<br \/>\nallows the dhchap options.  The nvme sysfs attributes that dereference<br \/>\nctrl-&gt;opts, such as hostnqn and address, evaluate their is_visible()<br \/>\ntest once at device_add() time and stay readable until<br \/>\ncdev_device_del().<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the Linux kernel, the following vulnerability has been resolved:<\/p>\n<p>nvme-fc: fix double free of fabrics options when nvme_add_ctrl() fails<\/p>\n<p>nvmf_create_ctrl() owns the fabrics options and frees them <\/p>\n","protected":false},"template":"","meta":[],"categories":[],"tags":[],"class_list":["post-24661","vuln","type-vuln","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/vuln\/24661","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/vuln"}],"about":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=\/wp\/v2\/types\/vuln"}],"wp:attachment":[{"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=24661"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=24661"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.54iter.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=24661"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}