One function swapped for another, twice, and a CVSS 9.8
Before: kfree(). After: kfree_rcu(). That’s the fix for CVE-2026-98432, repeated in two separate functions in two separate files. Read the CVSS score first and the bug sounds abstract: 9.8, critical, no privileges, no user interaction required. Read the diff first and the abstraction disappears. The bug is one function name, swapped for the wrong one, in exactly two places.
CVE-2026-98432 is a use-after-free in how the Linux kernel’s IPv4 TCP stack handles a connection’s IP options, present since a 2015 commit and now patched across every maintained stable branch.[1] NVD’s own CNA scored it 9.8, AV:N/AC:L/PR:N/UI:N: remote, low complexity, no privileges, no user interaction. Three weeks after disclosure, Red Hat’s own bulletin still names no fixed build, and Ubuntu’s own tracker still lists every supported release as vulnerable or work in progress.[2][3] An unreleased Anthropic research model reportedly turned this into a working, zero-click path to root in under an hour.[4] More on that claim later, carefully, because one detail attached to it online isn’t something Anthropic actually said.
I read the fixing commit myself, not a paraphrase of it. git.kernel.org returned an HTTP 403 to a direct fetch, so I read the identical commit, by hash, from the public mirror of the stable tree that Greg Kroah-Hartman maintains on GitHub.[5] I haven’t reproduced this in a lab. This piece is built from reading primary sources end to end, not from running an exploit, and that distinction matters for everything that follows.
// The actual kfree -> kfree_rcu swap, per the fixing commit (dossier
// S2). Surrounding code is an illustrative reconstruction of shape, not
// a literal excerpt.
net/ipv4/af_inet.c, inet_sock_destruct():
- kfree(opt)
+ kfree_rcu(opt, rcu)
net/ipv4/tcp_ipv4.c, tcp_v4_reqsk_destructor():
- kfree(opt)
+ kfree_rcu(opt, rcu)What ‘free it later’ is supposed to buy you
Here’s the invariant a lot of the kernel’s networking code leans on without thinking twice about it: a reader inside rcu_read_lock() gets to assume the memory it’s looking at is still there, because the convention is that writers defer their actual frees until every reader is provably done. That’s RCU, read-copy-update, and it’s the kernel’s answer to a hard problem: how do you let readers run fast and lock-free, while writers still get to delete things, without a reader ever touching memory that’s already gone?
A TCP request socket, the kernel’s half-open-connection object during the handshake, can carry IP options: attacker- or network-supplied data attached to the IP header of the SYN packet. The kernel stores those options behind a pointer (ireq_opt on the request socket, inet_opt once it’s promoted to a full socket) and reads them later, when it builds the SYN-ACK or ACK, inside ip_options_build(). That read happens under RCU. It has to: a second CPU handling a retransmitted SYN for the same connection can legitimately be inside that function at the same moment another CPU is tearing the connection down.[1][5]
Two destructors, one shortcut, zero grace period
Two call sites broke the contract RCU depends on: inet_sock_destruct() in net/ipv4/af_inet.c, and tcp_v4_reqsk_destructor() in net/ipv4/tcp_ipv4.c, both freed the same IP-options pointer with a plain kfree(), immediately, instead of the RCU-deferred kfree_rcu().[5] A plain kfree() makes memory available for reuse the instant it runs: no grace period, no waiting for readers to finish. Any CPU still inside ip_options_build(), reading the pointer that destructor just freed, is now reading memory that can be reallocated and rewritten out from under it.
The reader side used RCU: cheap, lock-free, and tolerant of concurrent frees as long as those frees are deferred. Two writer call sites didn’t hold up their end of that deal. That mismatch is exactly the kind of bug a WAF, an auth check, or any application-level control will never see, because it lives entirely inside the kernel’s own memory management, two one-line function calls deep in a destructor path.
// Illustrative timeline of the SYN-retransmit race (one of two trigger
// paths), reconstructed from the fixing commit's own description -- not
// a captured trace, and not exploit code.
CPU A (processing the connection) CPU B (retransmitted SYN)
---------------------------------- ----------------------------------
child socket created, inherits the
ireq_opt pointer -- not copied
tcp_v4_reqsk_destructor() runs:
kfree(ireq_opt) <-- freed now,
no grace period
still inside rcu_read_lock(),
ip_options_build() dereferences
the same pointer -- already
freed, possibly reusedThe SYN you retransmit, or the request socket you migrate
The fixing commit describes exactly two ways to land inside that window, and neither needs anything unusual. The first: tcp_v4_syn_recv_sock() hands a request socket’s IP options to its newly created child socket without copying them first, so a second CPU still processing a retransmitted SYN, ordinary TCP behavior, can read those options after they’re already freed.[1][5] Retransmitted SYNs happen on real networks constantly, so an attacker doesn’t need to invent this condition. They just need to time it.
The second path needs one more thing to be true: TCP request-socket migration explicitly enabled, either via net.ipv4.tcp_migrate_req=1 or an attached BPF program using SK_REUSEPORT_SELECT_OR_MIGRATE.[5] That’s not the default anywhere. When it’s on, a cloned request socket shares the same options pointer as the original, and the same destructor frees it out from under whichever one is still using it. If you run a mixed IPv4/IPv6 stack, one detail matters here: the fixing commit states IPv6 isn’t affected by the retransmit race, because IPv6 duplicates its request-socket options instead of sharing them. A related IPv6 migration issue is deferred to a separate patch.[1][5] This fix is IPv4 only.
Forty-eight minutes to a pointer, five more to root
Here’s the part of this story that actually went viral, and it’s worth getting exactly right. Anthropic’s own research post, under its Frontier Red Team byline, states that an unreleased, internal research model found and chained flaws into a remote, zero-click exploit against Linux servers running a multipath TCP listener: no credentials, no user interaction, root over the network.[4][6][7] The post cites CVE-2026-98432 by number. In Anthropic’s own words, it took 48 minutes to leak a kernel pointer, and five more minutes after that to reach shell command execution.[4]
One detail circulating alongside that claim isn’t actually in it. The codename ‘Project Glasswing’ doesn’t appear anywhere in Anthropic’s post. It traces back to an unverified secondary lead, not to Anthropic’s own words, and I’m not going to repeat it here as if Anthropic confirmed a name they never used. What is independently corroborated: two secondary outlets report the same CVE number, the same CVSS score, and the same 48-minute, 5-minute timing, and both trace back to Anthropic’s own post as the source.[6][7] One of them adds a line that deserves a pause before the next section: ‘the public record for that CVE describes an IPv4 TCP fix rather than a multipath TCP one, and the two are linked only by Anthropic’s post.’[7]
Red Hat’s advisory never says MPTCP. Neither does the patch.
That line is the whole story, and it’s where most of the secondhand advice goes wrong. Read the kernel’s own fixing commit, and it describes two trigger paths in plain IPv4 TCP terms, SYN retransmission and request-socket migration, with zero mention of Multipath TCP anywhere.[1][5] Read Red Hat’s own bulletin, RHSB-2026-016, and it’s the same: ‘systems accepting inbound TCP connections’ as the exposed population, a general race-condition description, no MPTCP in the precondition, the attack vector, or the mitigation guidance.[2] Two primary sources. Neither one mentions the thing half the internet is telling people to disable.
This is my own reading of those two sources side by side, not a sentence either one states outright, so I’ll say that plainly: an MPTCP listener is one way to reach this bug reliably. It is not the bug’s defining precondition. If a specific demonstrated exploit used MPTCP as its chosen entry point, and per the previous section that’s a plausible but still unverified detail of one reported exploit, that tells you about one practical path to winning the race, not about what the vulnerability itself requires.[4][7] Disabling MPTCP (net.mptcp.enabled=0) removes that one listener type as a trigger surface. It does not touch the underlying use-after-free, which stays fully reachable through ordinary, non-MPTCP TCP connection handling.
// What the vulnerability actually requires (b), versus what one
// reported demonstration used (c). Don't let (c) stand in for (b).
(b) Real preconditions, per the kernel's own commit and Red Hat's
advisory -- neither names MPTCP:
- A listener processing IPv4 TCP SYNs and retransmitted SYNs
(ordinary behavior, any TCP listener), OR
- Request-socket migration explicitly enabled
(net.ipv4.tcp_migrate_req=1, or a BPF
SK_REUSEPORT_SELECT_OR_MIGRATE program) -- not the default.
- Precise timing to win the race window.
- No credentials, no user interaction (PR:N/UI:N).
(c) What one reported demonstration reportedly used to trigger it
reliably:
- A reachable multipath TCP (MPTCP) listener.
Disabling MPTCP removes (c). It leaves (b) exactly where it was.Fixed in the kernel. Not yet in the kernel most people are running.
The upstream fix is in, and it’s dated precisely: stable branches are clear starting at 5.10.272, 5.15.223, 6.1.190, 6.6.159, 6.12.113, and 6.18.56, with 7.2.10 and the original fix tagged for mainline 7.3, all matching NVD’s own vendor-supplied data exactly.[1] That’s the easy part of this story. Whether the kernel under your actual workload matches one of those numbers is the part that takes checking, not assuming.
Red Hat’s own bulletin, last updated 2026-10-30, rates this ‘Critical impact’ but names no fixed build, no errata, and no CVSS score at all. It states only that Red Hat ‘is expediting the release of fixes.’[2] Ubuntu’s own tracker, as of this writing, lists every supported release from 16.04 through 26.10 as Vulnerable or Vulnerable, work in progress. Only 14.04, already out of standard support, is marked clear.[3] Red Hat’s interim mitigation, if you’re waiting on a build, is an nftables rule dropping inbound IPv4 TCP packets whose IP header carries options, which leaves ordinary TCP-layer options untouched and is explicitly not a substitute for patching.[2]
One more figure matters here, because headlines lean hard on urgency: as of this writing, CVE-2026-98432 is not in CISA’s Known Exploited Vulnerabilities catalog, checked two independent ways, and no source reviewed here documents confirmed exploitation in the wild.[8][9] That’s not the same claim as ‘safe to deprioritize.’ A 9.8 with no authentication and no user interaction earns real urgency on its own. It’s the honest scale: critical and unpatched in most distributions, not confirmed under active attack.
Unaffected starting at (NVD vendor data, matches the kernel's own
per-branch fixes exactly):
5.10.272 5.15.223 6.1.190 6.6.159
6.12.113 6.18.56 7.2.10 (original fix tagged for mainline 7.3)
Distro status, read from each vendor's own tracker as of this writing:
Red Hat (RHSB-2026-016): Critical impact. No fixed version or
errata named. "Expediting the release
of fixes." Last updated 2026-10-30.
Ubuntu (security tracker): every supported release, 16.04-26.10,
Vulnerable or Vulnerable, work in
progress. Only 14.04 (out of support)
is clear.What’s confirmed, and what’s still just a claim online
Two more things are circulating about this CVE, and I’m not going to resolve either one, because the record doesn’t let me. One version of events has Red Hat scoring this 5.9 initially, then 8.1, then 9.8, with the escalation partly attributed to Anthropic’s exploit-timing disclosure. Red Hat’s current bulletin simply doesn’t show a score, a vector, or any score history at all, so that specific 5.9-to-8.1-to-9.8 story is neither confirmed nor refuted by the one primary source available to check it against.[2]
The other is whether a working exploit proof-of-concept is actually public. One lead claims PoCs have been on GitHub since 2026-10-29; others say the exploit was never published. I didn’t locate or verify a working PoC repository either way. If someone tells you with confidence which of those is true, ask them for the repository, not the claim.
What to actually check this week
In the order that actually matters, not the order that makes the best headline.
Patch and verify, in order:
[ ] Confirm the kernel actually RUNNING, not the changelog: 5.10.272 /
5.15.223 / 6.1.190 / 6.6.159 / 6.12.113 / 6.18.56 / 7.2.10 or later.
[ ] If your distro hasn't shipped a fixed build yet, check its own
tracker directly -- don't trust a blog post's snapshot of it,
this one included. Apply Red Hat's interim nftables rule in the
meantime: drop inbound IPv4 TCP with IP header length > 5.
[ ] Check net.ipv4.tcp_migrate_req. If it's 1, or a BPF
SK_REUSEPORT_SELECT_OR_MIGRATE program is attached, the second
trigger path is open regardless of MPTCP.
[ ] Disable MPTCP if you want to (net.mptcp.enabled=0) -- a
reasonable attack-surface reduction, but record it as exactly
that, not as the fix for this CVE.
[ ] Don't use CISA's KEV list as a signal of how exposed you are
here. As of this writing this CVE isn't on it, and that's a
statement about confirmed in-the-wild use, not about your risk.The advisory said ‘use-after-free.’ The diff said exactly where.
Every advisory for this CVE says the same three words: use-after-free, kernel, IPv4. None of them, on their own, tell you that the actual mistake was two destructors choosing kfree() over kfree_rcu(), or that the MPTCP advice going around treats one exploit’s chosen door as the building’s only door. The diff told me that. The advisory didn’t, and wasn’t trying to: that’s not what an advisory is for.
That’s exactly the gap I look for in a secure code review: not whether a changelog says ‘fixed,’ but whether the actual diff addresses the root cause or just the one path someone happened to find first. If you maintain C or C++ code with the same kind of shared-ownership, deferred-lifetime pattern, RCU or otherwise, and want someone to read the destructor paths with that question in mind, that’s the kind of review I do. Tell me what you’re running, and we’ll scope it.
Sources
- [1] NVD: CVE-2026-98432 detail
- [2] Red Hat: RHSB-2026-016 security bulletin
- [3] Canonical (Ubuntu): CVE-2026-98432 security tracker
- [4] Anthropic (Frontier Red Team): “Discovering vulnerabilities with Claude”
- [5] Linux kernel stable tree: the fixing commit, read via its public GitHub mirror
- [6] AI Weekly: report on Anthropic’s chained-exploit claim
- [7] The Hacker News: report on Anthropic’s chained-exploit claim
- [8] CISA: Known Exploited Vulnerabilities Catalog, filtered query
- [9] CISA: Known Exploited Vulnerabilities Catalog, JSON feed
Need this checked in your own product?
Security code reviewWant to learn to find vulnerabilities like this yourself?
Secure code review course, one to one