CVE-2026-72139
In the Linux kernel, the following vulnerability has been resolved:
tcp: defer md5siginfo kfree past RCU grace period in tcpconnect
The md5+ao reconciliation in tcpconnect() (net/ipv4/tcpoutput.c)
has two symmetric branches:
if (needs_md5) {
tcpaodestroy_sock(sk, false);
} else if (needs_ao) {
tcpclearmd5_list(sk);
kfree(rcureplacepointer(tp->md5sig_info, NULL, ...));
}
Both branches free a per-socket auth-info object while the socket is
in TCPSYNSENT and is already on the inet ehash (inserted by
inethashconnect() in tcpv4connect()). Both branches are reachable
by softirq RX-path readers that load the corresponding info pointer
via implicit RCU before bhlocksock_nested() is taken.
The needs_md5 branch is fixed in the prior patch by re-introducing
the callrcu() free in tcpaodestroysock(): the equivalent per-key
loop runs inside tcpaoinfofreercu(), the RCU callback, so by the
time it frees each tcpaokey all softirq readers that captured the
container have already completed rcureadunlock().
The needs_ao branch is not symmetric in the same way. The container
free can be deferred via kfree_rcu(md5sig, rcu) -- struct
tcpmd5siginfo already has the required rcu member
(include/net/tcp.h:1999-2002), and the rest of the tree already does
this in the tcpmd5siginfo_add() rollback paths
(net/ipv4/tcp_ipv4.c:1410, 1436). But the per-key teardown is done
by tcpclearmd5_list() in process context BEFORE the container's
RCU grace period: it walks &md5sig->head and frees each
tcpmd5sigkey with bare hlist_del + kfree. A concurrent softirq
reader in _tcpmd5dolookup() / _tcpmd5dolookup_exact()
(tcp_ipv4.c:1253, 1298) walks the same list via
hlistforeachentryrcu() and races with that bare kfree on the
keys themselves -- a per-key slab use-after-free of the same class
as the TCP-AO bug, on the same race window.
Fix this in two halves:
- Convert the bare kfree() in tcpconnect() to kfreercu() so the
md5sig_info container joins the rest of the md5sig lifecycle.
The local-variable lift is mechanical and required because
kfree_rcu() is a macro that expects an lvalue.
- Make tcpclearmd5list() RCU-safe by replacing hlistdel +
kfree(key) with hlistdelrcu + kfree_rcu(key, rcu). struct
tcpmd5sigkey already carries the rcu member
(include/net/tcp.h:1995) and tcpmd5do_del()
(net/ipv4/tcpipv4.c:1456) already uses kfreercu, so this
restores the lifecycle invariant the rest of the file follows
rather than introducing a one-off.
The other caller of tcpclearmd5list() is tcpmd5destructsock()
(net/ipv4/tcp.c:412), which runs from the sock destructor when the
socket is already unhashed and unreachable; the extra grace period
there is unnecessary but harmless. Making the helper unconditionally
RCU-safe is the cleaner contract.
The needs_ao branch is not reachable by the userns reproducer used
to demonstrate the AO-side splat (the repro installs both keys but
ends up in the needs_md5 branch because the connect peer matches
the MD5 key, not the AO key); however the symmetric race exists
and a maintainer touching this code should not have to think about
which branch escapes RCU and which one does not.
[also credits to Qihang, who found that this races with tcp-diag]
Package Versions Affected
Automatically patch vulnerabilities without upgrading
CVSS Version



Related Resources
References
https://git.kernel.org/stable/c/33a1bee413628378fd036a4f2b17ba86b0bc560c, https://git.kernel.org/stable/c/b74cd55038905d5e74c1de109ab78a30b2ea0e1f, https://git.kernel.org/stable/c/da48b9bf1eb95a9cfd09d615ca58cfc2b03de369, https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/72xxx/CVE-2026-72139.json, https://nvd.nist.gov/vuln/detail/CVE-2026-72139, https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git