-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 NEFARIOUSPLAN-CANONICAL-V1 {"body_md":"The convention was already in place. In `net/ipv4/af_inet.c`, functions that read `inet->inet_opt` hold `rcu_read_lock()` and use `rcu_dereference()` to walk the pointer. The destructor in the same file does not. At line 158 of the pre-patch tree, `inet_sock_destruct()` calls `kfree(rcu_dereference_protected(inet->inet_opt, 1))`. A second destructor, in the request-socket teardown path, does the same for `ireq_opt`. Both free the object without waiting for readers to leave. Eric Dumazet's two-line patch on 2026-10-05 added `kfree_rcu` to both sites. The commit message says what the readers already said: this pointer lives behind an RCU grace period.\n\n## The race lives inside a single rcu_read_lock\n\n`tcp_v4_syn_recv_sock()` promotes a request socket to a child socket. It hands `ireq->ireq_opt` to the child without copying. The child now owns the allocation. When `inet_sock_destruct()` runs on that child later, it calls plain `kfree()` on `inet->inet_opt`.\n\nMeanwhile, `tcp_v4_send_synack()` on another CPU holds `rcu_read_lock()` and walks the same `ireq_opt` to build a SYN-ACK. The two paths overlap. The reader dereferences freed memory. KASAN sees it. On a production kernel without KASAN, the reader walks whatever object has replaced the slab allocation by the time the TX path reaches it. Most of the time the slab is quiet and the reader walks something that still looks like options. Some of the time it walks something that looks like controlled attacker bytes, and that is the chain.\n\nThe fix commit is `e3f33b0a1d89d738e2b913dab519226eaf76e15b`, dated 2026-10-05. The diff is minimal. `kfree` becomes `kfree_rcu` at both sites. CVE-2026-98432 was published 2026-10-15. Debian 13 shipped a build on 2026-10-16. Red Hat's advisory landed 2026-11-01. Ubuntu is still lagging at the time of this post.\n\n## The rule is visible in the same file\n\n`inet_sk_rebuild_header()` is one of the readers. It opens with `rcu_read_lock()`, calls `inet_opt = rcu_dereference(inet->inet_opt)`, uses the pointer, then unlocks. The reader protocol is explicit and the primitive name says so. `inet_sk_reselect_saddr()` is another, lower in the same file, which uses `rcu_dereference_protected(inet->inet_opt, lockdep_sock_is_held(sk))` because the writer side holds the socket lock. Both document the ownership rules at every call site.\n\nThe destructor on line 158 of the same file calls `kfree(rcu_dereference_protected(inet->inet_opt, 1))`. The second argument says \"I know this is a writer context, no lock is needed to dereference.\" That is true. It is also irrelevant to the question of whether a reader on another CPU still points into the allocation. The destruct protocol is a plain free. That gap is the bug, and it is visible inside a single compilation unit without any cross-file reasoning.\n\nRequest sockets have the same shape. The ehash table introduced in commit `079096f103fa` in Linux 4.4 holds `TCP_NEW_SYN_RECV` entries alongside established sockets. Readers on the TX side walk `ireq_opt` under RCU. The request-socket destructor frees it without one. When a listener accepts a connection and the child inherits `ireq_opt` without copy, the destructor on the child can free an object that a concurrent SYN-ACK path still reads.\n\nThere is a second trigger path introduced later. With `net.ipv4.tcp_migrate_req=1`, a request socket can be migrated from one listener to another when the original listener closes. The migration touches the same pointer, and under race it hands the window to the attacker without needing listener teardown. `BPF_SK_REUSEPORT_SELECT_OR_MIGRATE` opens the same door for anything using SO_REUSEPORT groups. Workloads that use reuseport migration for zero-downtime restarts are structurally more exposed to this bug than workloads that do not.\n\nThe CVSS numbers reflect the asymmetry of opinion. The kernel CNA assigned 9.8 with `AC:L`. Red Hat and several downstream vendors assigned 8.1 with `AC:H`. The attack is remote in both reads. The complexity question is whether the attacker can arrange the race reliably. Xinyang Ge, who reported the bug for Anthropic's Frontier Red Team, says the team built a remote-root MPTCP chain against a stock Debian 13 image on the strength of the race. That claim landed on 2026-10-27 and remains unverified by a public artifact.\n\n## MikkoAbudo wrote the race, nothing more\n\n`tcp_reqopt_race.c` from MikkoAbudo is two thousand lines of C that build raw Ethernet frames, pin the sender to a CPU, fragment the trip so the RX queue splits, then volley dup-SYN against ACK/RST with a skew sweep. It carries IP options in several flavours: Record Route, Timestamp, LSRR, SSRR, and an oversize RR variant it labels `BIGRR`. The Chinese comments name the mechanism exactly. `tcp_v4_syn_recv_sock()` hands `ireq->ireq_opt` to the child. The destructor frees it with plain `kfree()`. The reader on the TX path dereferences freed memory.\n\nWhat the file does not include is also legible from reading it. There is no KASLR leak. There is no reclaim primitive. There is no shell. The PoC trips the race. On a KASAN build it reports. On a stock kernel it usually produces nothing visible unless the attacker has already arranged the slab pressure to replace `ip_options_rcu` with something exploitable, and the PoC does not arrange that.\n\nThis is what an honest demonstration of a kernel-memory bug looks like. It shows the race exists. It leaves weaponisation to a different author and a different document.\n\n## thiscitze shipped a lab aid and a punch list\n\n`cve-2026-98432-root-shell` from thiscitze builds Linux 6.12.112 with aid patches, disables KASLR, and then names three things the researcher could not get past.\n\n- The seventy-two-byte `ip_options_rcu` reclaim does not reliably land on `saved_syn`.\n- Same-type reclaim yields no text pointer leak.\n- SSRR, RR, and Timestamp stores give four controlled bytes near the skb buffer, not an arbitrary write.\n\nThe README credits Anthropic's team for the only public claim of a working chain, dated 2026-10-27, and notes that the chain's code is not released. The file's own sentence: \"A stock-kernel weaponised chain is not publicly available as of 2026-11-02.\"\n\nThis is a defender's document wearing an attacker's filename. It names the blockers the chain would have to solve. It does not solve them. It is more useful than most things on the public list, because it is honest about where the research stops.\n\n## Ghost-Bx1 shipped a placeholder PoC calling /proc/kallsyms\n\n`cve-2026-98432-linux-tcp-uaf/exploit.c` from Ghost-Bx1 is a textbook placeholder PoC. It opens `/proc/kallsyms`, reads addresses for `commit_creds` and `prepare_kernel_cred`, patches a shellcode blob, and calls the result an exploit. Line 141 of the file says, in Portuguese: `\"Enderecos abaixo sao placeholders - substitua pelos reais.\"` Addresses below are placeholders. Replace with real ones.\n\nTwo problems. First, `/proc/kallsyms` returns zeroed addresses to unprivileged readers on every modern kernel. If the running process can read real values, the process is already root, and nothing else in the file matters. Second, CVE-2026-98432 is a remote bug. The vulnerable code path is reached by sending SYNs with IP options to a listening socket. A local privesc that assumes kallsyms access has not engaged the vulnerability at all. The file ships two separate dishonesties, and together they ship nothing.\n\nThe repository is packaged like a weapon. It reads like a tutorial for the wrong vulnerability. This is the shape the placeholder-PoC pattern keeps producing: a CVE-named filename, a shell payload, and one honest comment buried in the middle that says \"replace this with the real value,\" where the real value is either privileged or nonexistent.\n\n## Ninety repositories, three exploits, one real\n\nA scan on 2026-11-08 finds roughly ninety public repositories referencing CVE-2026-98432. The distribution is lopsided in a way that is now familiar.\n\nThe defensive tier is the bulk of it. There are nftables rules dropping TCP SYNs that carry IP options. One such rule is three lines: a chain on the `input` hook, a match on `ip hdrlength > 5`, a drop. There are eBPF filters at XDP. There are ansible playbooks that pin `net.ipv4.tcp_syncookies=1` and zero `net.ipv4.tcp_migrate_req`. There are kernel-installer wrappers. There are audit scripts that read `/proc/sys/net/ipv4/tcp_migrate_req` and `/sys/module/ipv4/parameters`. There are three independent repositories named a variant of `verification-gap-cve-2026-98432` that collate vendor advisories into one table, where Debian ships on 2026-10-16, Red Hat on 2026-11-01, and Ubuntu lags.\n\nThe offensive tier has three. MikkoAbudo trips the race. thiscitze builds the lab and lists the blockers. Ghost-Bx1 ships placeholders. That is the distribution.\n\nAnthropic's post on 2026-10-27 claims a remote-root MPTCP chain built from this bug, against an unmodified Debian 13 image. The code is not public. The claim is unverifiable in the ordinary sense. It is also plausible. Xinyang Ge, who found and reported the bug, works on that team, and the team's incentive is to publish proof of impact. The decision not to release the chain is its own kind of signal, and the signal is not reassurance.\n\n## The diff was the whole story\n\nThe commit message on `e3f33b0a1d89` is six lines long. It notes the two destructors needed a grace period. It describes no exploit. It names no victim. The tone is identical to every other RCU-conversion patch the subsystem has absorbed over the last decade.\n\nThe surprise is not the patch. It is that the destructor path shipped without the primitive for long enough that a researcher at a different company found it, filed it, and had time to write a chain. The reader protocol in `af_inet.c` has been RCU since well before 4.4. The destructor in `af_inet.c` has been plain-kfree the entire time. Both protocols were visible in the same file. Two destructors opted out of a convention their own file had already written down. The CVE exists because nobody who read that file flagged the mismatch until someone wanted to.\n\n## What to do on 2026-11-08\n\nPatch. If the kernel cannot be rebooted today, drop TCP SYNs carrying IP options at the edge. The nftables rule is three lines. The sysctl change is one line. Both have near-zero cost on production workloads, because inbound TCP SYNs with IP options are vanishingly rare outside of research traffic. Also consider zeroing `net.ipv4.tcp_migrate_req` on any host that does not need reuseport migration, because the migration path is a second trigger that does not require listener teardown.\n\nThe ninety-repo signal is not that the bug is overhyped. It is that the ecosystem produces defence faster than it produces offence, and that most of the offence produced in public is either unreliable or unrelated to the stated target. Track the Anthropic post if you track anyone. The rest is fog.","closing_line":"Patch, or drop the traffic. The worm is not in these repositories.","hook_md":"In `net/ipv4/af_inet.c`, readers of `inet_opt` hold `rcu_read_lock` and call `rcu_dereference`. The destructor in the same file calls plain `kfree`. CVE-2026-98432 is that mismatch, held open since Linux 4.4, closed on 2026-10-05 by a two-line patch that adds `kfree_rcu` to the two destructors that opted out.","post_id":850,"slug":"cve-2026-98432-two-destructors-skipped-kfree-rcu","title":"CVE-2026-98432: Two Destructors in net/ipv4 Used kfree(). The Sibling Writers Already Used kfree_rcu().","type":"initial","unreadable_sentence":"Two destructors opted out of a convention their own file had already written down."} -----BEGIN PGP SIGNATURE----- iHUEARYIAB0WIQRf0htP5+SjynlxywneZjl4jgkQJgUCavDQkQAKCRDeZjl4jgkQ JsA5AP9BuKmdKYWEFG6g5j4RMlO2/XqYOCIzRbR9numt8YOLCgEA1PcPGf665CD6 Tj4i9YA4D5um0isYNmvDPNT9yiUz6gE= =KXaD -----END PGP SIGNATURE-----