
Rust detector and local lab for CVE-2026-98432, a Linux IPv4 TCP use-after-free in inet_opt/ireq_opt: static ELF analysis, kprobe reachability checks, and a race trigger.
inet_opt / ireq_opt)Research PoC and local lab for a Linux kernel use-after-free reachable from a plain IPv4 TCP listener. This is a bug detector/trigger, not an exploit.
7.0.0-38-generic):
inet_sock_destruct() and tcp_v4_reqsk_destructor() free the options with
kfree() instead of kfree_rcu().kfree path is reachable at runtime (kprobes).free -> use events (via=reqsk) in one old run, and could not reproduce
them reliably.tcp2kaslr reference repo also stops at the bug trigger; later stages are
not published.CONFIG_KASAN is off and KFENCE sampling is disabled.tcp2kaslr)MPTCP is not required. The proven path is plain IPv4 TCP:
ireq_opt).accept()s the child, sets SO_LINGER(0) and closes it.
inet_sock_destruct() frees the options with kfree().Our earlier trigger was wrong in two ways: the victim never accepted/closed the
child (free_sock=0), and we sent a RST instead of relying on the server close.
The current code adds the correct pieces:
listen --accept-close — accept + SO_LINGER(0) + close.--sync — ACK + duplicate SYN concurrently, no RST, Record Route options.The fix upstream is exactly:
- kfree(rcu_dereference_protected(inet->inet_opt, 1));
+ kfree_rcu(rcu_dereference_protected(inet->inet_opt, 1), rcu);
and the same change in tcp_v4_reqsk_destructor().
| Path | What |
|---|---|
rust-poc/ | the native Rust PoC: static ELF analysis, kprobes, trigger modes |
lab/ | Alpine + PHP loopback victim for the old local tests |
scripts/ | local repro helpers, authorized audit helpers |
docs/FINDINGS.md | what was tested, what was confirmed, what was not |
docs/EXPLOITATION-NOTES.md | scope: bug trigger only, no RCE |
docs/evidence/ | sanitized sample evidence |
cd rust-poc
cargo build --release
# static analysis (no root)
./target/release/cve-2026-98432 static
# runtime reachability (root, tracefs)
sudo ./target/release/cve-2026-98432 runtime
# correct local vector: accept+close server + --sync trigger
cd ..
PASSES=1 ./scripts/repro-sync.sh 8083
repro-sync.sh starts the --accept-close victim on 127.0.0.1:8083, runs the
sync trigger with sudo, and prints free_sock, use_build and the UAF
verdict. A clean run is not proof of safety: the rate is roughly 1e-7 per
attempt and the events cluster.
If artifacts/ is missing, static/runtime fall back to /boot; the
legacy scripts/cve-2026-98432.sh --static can generate the kernel artifacts.
With KASAN, the kernel reports a slab-use-after-free in
inet_csk_route_req / ip_options_build with the free stack in
inet_sock_destruct / inet_release. The tcp2kaslr harness reaches that in
about 134k attempts with KASAN; on a normal kernel the same race was seen only
twice in ~48M attempts.
Only test kernels and hosts you own or are explicitly authorized to test. The trigger is a kernel race and can crash the host. Use a disposable VM with a snapshot.
offline-ant/tcp2kaslr — bug trigger, KASAN evidence, analysis:
https://github.com/offline-ant/tcp2kaslripv4: free inet_opt and ireq_opt after an RCU grace period
(Eric Dumazet), kfree → kfree_rcu in net/ipv4/af_inet.c and
net/ipv4/tcp_ipv4.c.MIT. See LICENSE.