CVE-2026-98432 is a lifetime bug, not a parsing bug. The IPv4 TCP stack let lockless RCU readers use an IP options buffer that two destructors freed with plain kfree(). The upstream fix swaps that call for kfree_rcu(). Understanding why that works also tells administrators which settings reduce exposure until their distribution ships a fixed kernel.
What Was Disclosed
The flaw was published to NVD on October 15, 2026. The Linux kernel CVE team rates it 9.8 under CVSS 3.1: reachable over the network, with no privileges and no user interaction required. It is classified as a race condition (CWE-362) that leads to use-after-free (CWE-416).
Vendors score it lower. Red Hat and SUSE list 8.1, and Amazon Linux scores it between 7.0 and 8.1. The gap reflects how much real exploitation depends on timing and configuration.
The bug traces back to commit 079096f103fa ("tcp/dccp: install syn_recv requests into ehash table"), which landed in Linux 4.4. Most kernels from 4.4 through current 6.x branches are therefore affected.
The Ownership Handoff That Broke the Contract

Since 4.4, half-open request sockets live in the established hash table (ehash). This lets the kernel process their packets without taking the listener lock. Readers protect themselves with rcu_read_lock() instead. RCU makes that cheap under one rule: no code may return a shared object to the allocator while a reader might still hold a pointer to it.
When the handshake completes, tcp_v4_syn_recv_sock() builds the child socket. It moves the IP options pointer to the child instead of copying the buffer:
newinet->inet_opt = ireq->ireq_opt;
Now consider two CPUs handling the same connection:
- CPU 1 handles a retransmitted SYN in
tcp_check_req(). Underrcu_read_lock(), it reachestcp_v4_send_synack(), whereip_options_build()readsopt->optlenmore than once. - CPU 0 tears down the new child socket, for example after a failed handshake. TCP child sockets do not carry the
SOCK_RCU_FREEflag, soinet_sock_destruct()callskfree()immediately. - The slab object goes back to the allocator while CPU 1 is between its two reads. The second read touches freed memory, which may already belong to another object.
A second path exists in request socket migration. Migration is enabled by net.ipv4.tcp_migrate_req=1 or by an eBPF program using BPF_SK_REUSEPORT_SELECT_OR_MIGRATE. In this path, reqsk_timer_handler() clones a request socket with inet_reqsk_clone(), and the clone shares ireq_opt with the original. When the clone is released through tcp_v4_reqsk_destructor(), the old code again used synchronous kfree(). Another CPU could still be reading the options through the original socket at that moment.
IPv6 avoided the main path because tcp_v6_syn_recv_sock() copies the options instead of sharing the pointer. On October 27, Anthropic published research crediting Xinyang Ge. It described Multipath TCP listeners as a way to make the race much easier to hit, because MPTCP produces frequent parallel handshakes and migrations.
Why kfree_rcu() Is the Right Fix
Eric Dumazet's upstream patch, "ipv4: free inet_opt and ireq_opt after an RCU grace period," changes only the two destructors:
- kfree(rcu_dereference_protected(inet->inet_opt, 1));
+ kfree_rcu(rcu_dereference_protected(inet->inet_opt, 1), rcu);
The same change applies to ireq->ireq_opt in tcp_v4_reqsk_destructor().
kfree_rcu() does not free memory on the spot. It queues the object until every CPU has passed through a quiescent state. At that point, every rcu_read_lock() section that existed when the free was requested has finished. CPU 1's ip_options_build() is done, and no valid pointer to the buffer remains.
The alternatives were worse:
- Adding a lock to the SYN path would undo the scalability gained in 4.4.
- Copying options on every handshake would add an allocation per connection, for a feature that ordinary traffic almost never uses.
The fix is also easy to backport. struct ip_options_rcu already embeds an rcu_head named rcu, so deferring the free requires no change to the structure layout.
The general lesson applies to kernel and userspace code alike. When a pointer is handed between owners, the free path must respect the weakest synchronization that any reader relies on. Moving ownership without copying is fine. Freeing as if no lockless reader exists is not.
What Administrators Should Do Now
The fix reached maintained stable branches on October 24, 2026. Amazon Linux shipped it the same day for kernel6.18, and Debian and Oracle Linux have released patched kernels.
As of late October, the remaining status is mixed:
- Ubuntu: 22.04, 24.04, and 26.04 LTS are listed as work in progress.
- RHEL 8, 9, and 10: Affected, with fixes deferred to a later update.
- RHEL 7: Not affected, because it lacks the ehash change.
Check your distribution's tracker rather than relying on version numbers alone.
Until you can reboot into a fixed kernel, reduce the reachable attack surface. First, turn off MPTCP and request migration:
# Turn off MPTCP and request migration unless you rely on them
sysctl -w net.mptcp.enabled=0
sysctl -w net.ipv4.tcp_migrate_req=0
Save both settings in a file under /etc/sysctl.d/ so they persist across reboots. Disabling migration can affect zero-downtime listener handoffs, so confirm that no service depends on it first.
Next, drop inbound SYN packets that carry IPv4 options. Ordinary Internet TCP almost never uses options such as LSRR, SSRR, or Record Route. Dropping these packets means the vulnerable buffer is never allocated for untrusted peers. In nftables, an IPv4 header longer than five 32-bit words means options are present:
ip hdrlength > 5 tcp flags & (syn | ack) == syn drop
If your iptables build includes the ipv4options match, iptables -A INPUT -p tcp --syn -m ipv4options --any-opt -j DROP has the same effect.
These steps narrow exposure, but they do not fix the lifetime bug. Install the vendor kernel, reboot, and run uname -r to confirm that the patched kernel is the one running.




Loading comments…