Linux Kernel TCP Crash CVE-2026-99288: No Fix Yet in 4 LTS Branches

CVE-2026-99288TCPLinux kernelLong-term kernelsarm64

On 10 November 2026 the kernel’s CVE team published CVE-2026-99288, a race in the code that matches every incoming TCP packet to its connection. The fix commit comes with a kernel panic triggered by a proof of concept, and the code dates back to Linux 4.4, released in 2016. The fix reached 6.12, 6.18 and 7.3 the same day. The older long-term kernels, 5.10, 5.15, 6.1 and 6.6, do not have it yet.

“Every box we ship speaks TCP” is the right first reaction, and for once it is close to the right answer: there is no exotic feature to switch on here. But the details still matter: what has actually been demonstrated (a crash, not code execution), which part of the attack is under an attacker’s control, and why on arm64 the release that “has the fix” carries only half of it. This post goes through the bug, the kernels that carry it, who can reach it, and how to decide quickly.

Developing story. We will update this article when the fix reaches the remaining long-term branches. Last checked: 11 November 2026, against the releases of 6 November (5.10.274, 5.15.225, 6.1.192, 6.6.162) and 10 November (6.12.117, 6.18.63, 7.3.4). The fix is in the stable queue for the next 5.10, 5.15, 6.1 and 6.6 releases.

The facts

CVE-2026-99288
Component__inet_lookup_established() and __inet6_lookup_established(), the lockless lookup of established TCP connections for IPv4 and IPv6
Bug classRace condition: memory of a closed socket is reused for a new one while a lookup still holds the old address, and the lookup returns the new, not yet registered socket
Demonstrated impactKernel panic (NULL pointer dereference in tcp_v4_rcv()) in the packet receive path. No code execution shown
Kconfig gateNone for the bug itself. The published crash uses SYN cookies (CONFIG_SYN_COOKIES, also selected by the netfilter SYNPROXY targets)
Introduced by079096f103fa “tcp/dccp: install syn_recv requests into ehash table”, first released in 4.4
Affected kernelsEvery release from 4.4 up to 6.12.116, 6.18.62 and 7.3.3; all 5.10, 5.15, 6.1 and 6.6 releases to date; every end-of-life series since 4.4, such as 5.4 and 7.2, which will not be fixed
Fix4e2b3ef19e0e “inet: reject unhashed sockets during established lookups”, in mainline since 7.4-rc2, with Cc: stable. A second patch, 5b5d8f351dd9, fixes memory ordering on arm64 and other weakly ordered CPUs
Stable and long-term statusFixed in 6.12.117, 6.18.63 and 7.3.4 (first patch only). Queued for 5.10, 5.15, 6.1 and 6.6
ScoreNo CVSS from the kernel CNA; NVD status “Received”, no NVD analysis yet
ExploitThe reporter’s proof of concept is not public; not in CISA KEV

How the bug works

For every incoming TCP segment, the kernel looks up the connection it belongs to in a hash table. To keep that fast, the lookup takes no locks. The memory for TCP sockets comes from caches marked SLAB_TYPESAFE_BY_RCU: when a socket is closed, its memory may be handed out again right away, but only to another socket of the same kind. A lookup that found the old socket can therefore end up holding a different, newer socket at the same address.

The lookup knows this and protects itself: after taking a reference, it checks the connection’s addresses and ports again. What it did not check is whether the socket it now holds is actually registered in the hash table. If the new socket happens to have the same addresses and ports, the second check passes. The fix adds the missing test:

 	if (unlikely(!refcount_inc_not_zero(&sk->sk_refcnt)))
 		goto out;
-	if (unlikely(!inet_match(net, sk, acookie,
-				 ports, dif, sdif))) {
+	if (unlikely(hlist_nulls_unhashed_lockless(&sk->sk_nulls_node) ||
+		     !inet_match(net, sk, acookie, ports, dif,
+				 sdif))) {
 		sock_gen_put(sk);
 		goto begin;
 	}

The reporter’s proof of concept makes the memory come back as a request socket created from a SYN cookie. That kind of socket is never registered and has no listener attached. The lookup returns it anyway, and tcp_v4_rcv() then reads the listener’s state through a NULL pointer. The result is a panic in interrupt context, which takes the whole machine down. In the published trace, the crashing packet arrived through the virtual machine’s network driver, not over loopback.

There is no real gate

Unlike most kernel CVEs we write about, this one is not behind a driver, a filesystem or an opt-in feature. The lookup runs for every TCP packet on every kernel with networking. The only switch in the published crash is SYN cookies:

# Built in? (also selected by the netfilter SYNPROXY targets)
grep SYN_COOKIES .config                 # CONFIG_SYN_COOKIES=y

# On by default: cookies are sent when a listener's SYN queue overflows
sysctl net.ipv4.tcp_syncookies           # net.ipv4.tcp_syncookies = 1

With the default setting of 1, a server only falls back to SYN cookies when the SYN queue of a listening socket overflows. A remote client can force that with a SYN flood, which is the textbook attack SYN cookies exist to absorb. Turning cookies off is not a good trade: it removes the demonstrated crash route but reopens the door to SYN floods, and the lookup bug itself stays in place.

Present and, this time, mostly reachable. A kernel built without CONFIG_SYN_COOKIES lacks the code path the published crash uses, but the lookup flaw is in the core of TCP. In his review, the TCP maintainer names TCP Fast Open, migrated request sockets and new connection sockets before they are registered as further cases of sockets that hold a reference without being in the table. We would not mark a product not_affected on the build configuration alone.

Two attackers, two answers

Remote clientLocal, unprivileged user
What they controlThe packets of their own connections: they can reuse the same addresses and ports, and they can push a listener into SYN-cookie mode with a SYN floodBoth ends of the connection over loopback, the timing of their own sockets, and unlimited retries
What they need from youA TCP port on the device that they can reachNothing beyond a shell; TCP over loopback needs no privileges
The raceA short window on the target’s CPUs between a lookup and the reuse of the same memory, which the attacker cannot observeThe same window, with far better control over both sides
Realistic outcomeA kernel crash if the race is won. Whether a purely remote attacker can win it has not been shown: the trace shows the crashing packet arrived over a network interface, but not where the other side of the race ran, or how many attempts it tookA crash. Privilege escalation has not been shown (see below)

For an appliance, the remote case is the one that matters: if a remote attacker can win this race, a device that answers TCP on a reachable port goes down, and it reboots or hangs until someone power-cycles it. For devices that run untrusted local code, the local case is the easier one.

Is it a local root risk? Compared with exploited bugs of the same class

A memory reuse race in the kernel is the raw material of many privilege escalation exploits, so the question is fair. We compared this bug with race-condition use-after-frees in our exploit database that have a working public exploit, and with its sibling from last week in the same TCP code. The score is our estimate of how hard each bug is to turn into root, from 1 (easy) to 5 (hard).

CVEFreed memoryUse triggered byExploitScore
CVE-2026-23272
nf_tables race
Generic kmalloc-cg-64; refilled from the same cacheAttacker’s syscalls and packetskernelCTF, probabilistic2 / 5
CVE-2026-46242
Bad Epoll
Dangling struct file in a type-stable cache; cross-cacheAttacker’s close() racekernelCTF + PoCs, ~99 %3 / 5
CVE-2024-26583
kernel TLS race
Generic kmalloc-512; cross-cacheAttacker’s recv()/close() racekernelCTF, “very unreliable”4 / 5
CVE-2026-99270
TCP request timer
request_sock, type-stable cache; cross-cacheThe retransmit timer, on the kernel’s scheduleNone4–5 / 5
CVE-2026-99288
this bug
Socket in a type-stable cache; in the crash, a different live socketThe attacker’s incoming packetCrash PoC, private4–5 / 5

One property favours the attacker: here the kernel touches the reused memory when a packet arrives, so the attacker decides when to try, as in the exploited bugs at the top of the table. Another property works against them. In the published crash, the stale pointer does not land on freed memory but on a different, live socket: the kernel treats one socket as another and then fails on a missing field. The patch also closes error paths where a request socket could be freed directly while a stale lookup still held a reference to it, so freed memory is not ruled out. In our reading, turning this into control over memory would need more than the published crash shows.

The reporter’s first patch version described a second route that did end in a use-after-free of a full socket. That result came from a test kernel with artificial delays added to widen the race. The TCP maintainer questioned whether the sequence is possible on a real kernel, and the paragraph was removed from the final commit. We do not count it as demonstrated.

Our reading: a crash today, plausibly from the network; privilege escalation unproven. As of 11 November 2026, our exploit database, which tracks public proof-of-concept repositories and the kernelCTF submissions, has no exploit for CVE-2026-99288. The difficulty scores are our own assessment from the public write-ups and the patch discussion, not a measurement.

The timeline NVD won’t show yet

DateEvent
Oct 2015079096f103fa puts request sockets into the connection hash table; first released in 4.4
2017–2020arm64 (4.15) and 32-bit ARM (4.16), then every architecture with 5.5, switch to a reference-count implementation that is not a full memory barrier on weakly ordered CPUs (the problem the second patch fixes)
21 Oct 2026Hyunwoo Kim reports the bug with a first patch to the networking list
29 Oct 2026Fifth version, a two-patch series; Eric Dumazet reviews the first patch, Kuniyuki Iwashima both
3 Nov 2026Both patches applied to the networking tree
5 Nov 2026Merged into mainline; released in 7.4-rc2 (8 November)
6 Nov 2026The first patch is queued for 5.10, 5.15, 6.1, 6.6, 6.12, 6.18 and 7.3. The same day, 5.10.274, 5.15.225, 6.1.192 and 6.6.162 are released without it
10 Nov 20266.12.117, 6.18.63 and 7.3.4 released with the first patch. The second patch is queued for those three branches and fails to apply to 5.10, 5.15, 6.1 and 6.6
10 Nov 2026CVE-2026-99288 published by the kernel CNA, without a CVSS score; NVD status “Received”

Which kernels really have it

This time the CVE record and the code largely agree. The record marks everything from 4.4 onward as affected, with 6.12.117, 6.18.63 and 7.3.4 as the first fixed releases. It has no entry for mainline, so tools that follow it may still flag 7.4-rc2 and later, and it tracks only the first patch. We checked the source of the latest release of each branch: the new check is in 6.12.117, 6.18.63 and 7.3.4, and missing from 5.10.274, 5.15.225, 6.1.192 and 6.6.162. 5.4 reached end of life with 5.4.302 in December 2025 and will not get the fix; neither will 7.2, which ended with 7.2.15 on 1 November and is still what Debian testing ships.

The second patch and arm64

The fix came as two patches. The first adds the missing check; the second makes sure that, on CPUs that may reorder memory accesses, the check actually reads the new socket’s state and not the old one’s. During review, the networking list’s automated reviewer pointed out that the first patch alone does not close the race on such CPUs, and asked whether both should be backported together; the question went unanswered. x86 does not reorder in this way; arm64, 32-bit ARM, RISC-V, PowerPC and MIPS, among others, can.

The stable releases of 10 November carry only the first patch. The second is queued for the next 6.12, 6.18 and 7.3 releases. For 5.10, 5.15, 6.1 and 6.6 it failed to apply: those branches lack the reference-count helpers it uses and need an adapted backport. If you ship arm64, plan for two updates, or carry both patches yourself.

Was the risk visible at merge?

Yes. Both patches carry Cc: stable, and the commit message includes the panic. The first patch reached three stable branches within a week of its mainline merge. The remaining gap is simply the release schedule: 5.10, 5.15, 6.1 and 6.6 were released the same day the patch was queued, so it is expected in their next releases.

What the distributions say

Status of their trackers on 11 November 2026:

DistributionStatusTheir rating
Debian12 “bookworm” (6.1.190), 13 “trixie” (6.12.116), testing (7.2.15) and unstable (7.3.3): vulnerable. Fixed version recorded for unstable: 7.3.4—
UbuntuNeeds evaluation, every release from 14.04 to 26.10Priority medium
Red HatBugzilla entry open, no product status yetSeverity high, titled “Denial of Service via race condition in TCP socket lookup”
SUSENo CVE page yet—

Red Hat’s title matches what has been demonstrated: a denial of service.

What your tools will show

Neither the kernel CNA nor NVD has scored the CVE yet, and NVD has no CPE version range. Dependency-Track and other NVD-fed scanners will show it unscored, or not match it to your kernel, until NVD’s analysis lands. Scanners that read the CVE record directly will flag every 5.10, 5.15, 6.1 and 6.6 release, which is correct today. They will not tell you that an arm64 6.12.117 still waits for the second patch.

30-second checklist

  1. Is your kernel version in the affected range? Before 4.4: not_affected, justification code_not_present. 6.12.117, 6.18.63, 7.3.4 and later in their branches: state resolved on x86. On arm64 and other weakly ordered CPUs, keep it open until you ship the release with the second patch, which is queued for the next 6.12, 6.18 and 7.3 releases. Otherwise continue.
  2. Who can reach TCP on the device? If no listening port is reachable from untrusted networks (for example, the device sits on an isolated management network behind a firewall you control) and no untrusted local code runs on it (third-party apps, containers, other users), you can record not_affected with justification protected_at_perimeter for now, and patch with your next regular update. A firewall does not stop the local route over loopback. Otherwise continue.
  3. Patch. Take the next 5.10, 5.15, 6.1 or 6.6 release once it is out, or carry the first patch now: backports for 5.10 and 5.15 have been posted to the stable list, and the patch is in the stable queue for 6.1 and 6.6. Do not switch off SYN cookies as a workaround.

Sometimes the answer is “patch”

Most kernel CVEs fail one of the checks above: the code is not built, not loaded, or not reachable by the attacker who matters. This one passes most of them, and that is useful to know too: it tells you which CVE of this week deserves the update slot. Upload your .config to KernelScan and every kernel CVE is checked against your exact kernel version, the long-term backports and the options your product builds. On paid plans, the security factors you declare for a product, such as whether it exposes TCP services to untrusted networks, feed into the assessment of each CVE.

References: CVE record · NVD · fixes 4e2b3ef19e0e and 5b5d8f351dd9 · introducing commit 079096f103fa · patch discussion (v5) and first version with review · ip-sysctl documentation (tcp_syncookies) · distribution trackers as linked in the table above. The per-branch fix status (checked in the source of each release tag and in the stable queue), the threat-model split and the exploitability assessment are our own analysis as of 11 November 2026.