CVE-2026-89968 Overview
CVE-2026-89968 is a Linux kernel vulnerability in the NVMe over TCP target subsystem (nvmet-tcp). The function nvmet_tcp_handle_h2c_data_pdu() accepts Host-to-Controller (H2CData) Protocol Data Units without verifying that the target actually solicited the data by sending a Ready-to-Transfer (R2T) PDU. A remote attacker can send an unsolicited H2CData PDU to trigger a double completion of the same command, corrupting the response list into a self-referential node. The result is either a kernel BUG when CONFIG_DEBUG_LIST is enabled or an infinite loop that wedges the nvmet_tcp workqueue as a soft-lockup. The flaw is remotely triggerable and requires no authentication on subsystems configured with allow_any_host.
Critical Impact
Unauthenticated remote attackers can crash or hang Linux NVMe/TCP storage targets, disrupting availability of block storage services exported over the network.
Affected Products
- Linux kernel nvmet-tcp NVMe over Fabrics TCP target driver
- Linux distributions shipping affected upstream kernel versions with NVMe/TCP target support enabled
- Storage systems and appliances exposing NVMe over TCP targets with allow_any_host subsystems
Discovery Timeline
- 2026-09-16 - CVE-2026-89968 published to the National Vulnerability Database
- 2026-09-16 - Last updated in NVD database
Technical Details for CVE-2026-89968
Vulnerability Analysis
The NVMe/TCP protocol uses a request-response flow where the target issues an R2T PDU to invite the host to transmit write data via H2CData PDUs. The target driver nvmet_tcp_handle_h2c_data_pdu() validates only that the incoming Transfer Tag (TTAG) maps to an in-range command index and that data buffers are mapped. It does not verify that the target actually sent an R2T for the command referenced by the TTAG.
A remote host can submit a write command that takes the R2T path and then transmit an H2CData PDU before the target emits the R2T. The premature data path completes the command early. When the command subsequently fails synchronously, for example through a length mismatch caught by nvmet_check_transfer_len(), the command completes a second time. Each completion invokes nvmet_tcp_queue_response(), adding the same command to queue->resp_list twice while it remains linked. This makes the fix a Denial of Service class issue rooted in missing state validation.
Root Cause
The root cause is a missing state check in the NVMe/TCP target's PDU handling logic. The driver tracks command tags and buffer mappings but does not track whether an R2T has been transmitted for a given command. The second llist_add() call on the same node causes lentry->next to point to itself. nvmet_tcp_process_resp_list() then walks the self-referential list node and adds the command to resp_send_list twice, corrupting subsequent list state.
Attack Vector
Exploitation requires only network access to a Linux NVMe/TCP target that exports a subsystem with allow_any_host enabled. The attacker initiates a standard NVMe/TCP connection, submits a write command, and races the target by sending an H2CData PDU with the command's tag before the target issues its R2T. The malformed sequence triggers the double completion, producing either a kernel BUG (with CONFIG_DEBUG_LIST) or a hung workqueue and soft-lockup. No authentication and no user interaction are required.
Verified proof-of-concept code is not available in the enriched data. See the upstream fix commits for the exact patch behavior, including the new flag tracking R2T transmission and reject logic in nvmet_tcp_handle_h2c_data_pdu().
Detection Methods for CVE-2026-89968
Indicators of Compromise
- Kernel log entries reporting list_add double add warnings originating from nvmet_tcp code paths
- Soft-lockup or hung task messages referencing the nvmet_tcp_wq workqueue or nvmet_tcp_process_resp_list
- NVMe/TCP target subsystems becoming unresponsive to legitimate initiators with no corresponding administrative action
- Unexpected nvmet-tcp connection resets or fabric session terminations shortly after new initiator connections
Detection Strategies
- Monitor kernel ring buffer output via dmesg and journald for nvmet_tcp warnings, BUG reports, and workqueue stalls
- Correlate NVMe/TCP target service degradation with network-level connection events from untrusted or unexpected source addresses
- Inspect NVMe/TCP session logs for initiators that transmit H2CData PDUs without a preceding R2T from the target
Monitoring Recommendations
- Ingest kernel telemetry and syslog data into a centralized analytics platform to alert on nvmet_tcp panic strings and soft-lockup patterns
- Track network flows to TCP port 4420 or other configured NVMe/TCP ports and alert on connections from sources outside authorized storage networks
- Baseline healthy NVMe/TCP throughput and latency, then alert on sudden target unresponsiveness suggestive of a wedged workqueue
How to Mitigate CVE-2026-89968
Immediate Actions Required
- Apply the upstream Linux kernel patches referenced below to affected hosts running the NVMe/TCP target driver
- Restrict network access to NVMe/TCP target ports so that only authorized initiators on trusted storage networks can connect
- Disable allow_any_host on any exported subsystem where it is not strictly required and require explicit host NQN allowlisting
- Audit exported NVMe subsystems and unload the nvmet_tcp module on hosts that do not need to serve NVMe over TCP
Patch Information
The upstream fix tracks whether an R2T has been transmitted for a command and rejects an H2CData PDU that arrives before it. The tracking flag is cleared on command reuse in nvmet_tcp_get_cmd() and persists across multiple H2CData PDUs of a single solicited transfer. Fix commits are available in the stable kernel tree: Linux Kernel Commit 02341ee, Linux Kernel Commit 14beef7, Linux Kernel Commit 1cbed7, Linux Kernel Commit 2b71f91, Linux Kernel Commit 9fb5271, Linux Kernel Commit cade124, Linux Kernel Commit cf1484d, and Linux Kernel Commit db62b35.
Workarounds
- Isolate NVMe/TCP target interfaces on a dedicated storage VLAN and enforce firewall rules limiting inbound connections to known initiator addresses
- Replace allow_any_host configurations with explicit host NQN allowlists on each exported subsystem to require known initiators
- Stop and disable the nvmet_tcp service or unload the kernel module on systems that do not require NVMe over TCP exposure until patches are applied
# Configuration example: restrict NVMe/TCP target exposure until patched
# 1. Disable allow_any_host on an exported subsystem
echo 0 > /sys/kernel/config/nvmet/subsystems/<subsysnqn>/attr_allow_any_host
# 2. Explicitly allow only known host NQNs
ln -s /sys/kernel/config/nvmet/hosts/<hostnqn> \
/sys/kernel/config/nvmet/subsystems/<subsysnqn>/allowed_hosts/<hostnqn>
# 3. Firewall NVMe/TCP port (default 4420) to trusted initiators only
iptables -A INPUT -p tcp --dport 4420 -s <trusted_initiator_cidr> -j ACCEPT
iptables -A INPUT -p tcp --dport 4420 -j DROP
# 4. If NVMe/TCP target is not required, unload the module
modprobe -r nvmet_tcp
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

