From: Daehyeon Ko <4ncienth@gmail.com>
To: netdev@vger.kernel.org
Cc: David Ahern <dsahern@kernel.org>,
Ido Schimmel <idosch@nvidia.com>,
"David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Simon Horman <horms@kernel.org>,
Willem de Bruijn <willemdebruijn.kernel@gmail.com>,
Vladislav Yasevich <vyasevic@redhat.com>,
linux-kernel@vger.kernel.org
Subject: [PATCH net v3] udp: revalidate socket family before publishing an IPv6 cork
Date: Wed, 2 Sep 2026 10:04:05 +0900 [thread overview]
Message-ID: <20260902010408.1057857-1-4ncienth@gmail.com> (raw)
udpv6_sendmsg() prepares IPv6 flow and route state before taking the socket
lock when a datagram is corked. IPV6_ADDRFORM takes the same lock, but it
can convert the socket to AF_INET during that lockless preparation because
no cork has been published yet.
If the conversion wins, udpv6_sendmsg() can publish an AF_INET6 cork on an
AF_INET socket. Uncorking through the IPv4 operations then writes a
20-byte IPv4 header into the 40-byte IPv6 reservation while the retained
IPv6 dst routes the skb through ip6_output(). ip6_finish_output2()
consequently consumes the unwritten 20-byte tail.
An unprivileged reproducer triggered the mixed state on 12 of 10,000
sockets. KMSAN reported an uninitialized-value read in
ip6_finish_output2() on three fresh boots, with the allocation origin in
__alloc_skb() through __ip6_append_data(). The same process recovered the
20-byte region from the TX timestamp error queue on all three boots; one
contained recognizable nonzero stale heap data.
There is also socket state to handle before cork publication. A sendto()
with an AF_UNSPEC address clears its explicit destination and later selects
the stored peer with connected set. On an ADDRFORM-eligible socket with a
mapped peer and a suitable IPv6 route, ip6_sk_dst_lookup_flow() can
therefore publish an IPv6 dst in the socket cache. A native connected send
racing a new mapped connect can reach the same store. Revalidating only
after lock_sock() prevents the mixed cork, but does not cover the non-cork
path or undo a dst stored before conversion.
Close both orderings. After a connected IPv6 lookup, reject a socket which
has already been converted and reset any dst the lookup may have stored.
When UDP ADDRFORM publishes the AF_INET family, reset the socket dst after
the family write. Thus, if the send stores and checks first, the later
conversion clears the cache; if conversion and its reset happen first, the
send observes AF_INET after its store and clears it. Keep the locked
family check before cork publication to prevent the original mixed cork.
With a mapped loopback route supplied before dropping to uid 65534 with no
capabilities, an unpatched KASAN control reached ip6_sk_dst_store_flow()
from the AF_UNSPEC send. On patched current net, the sequential conversion
released that dst from do_ipv6_setsockopt(). Corked and non-corked races
both reached the dst store, then returned EAFNOSUPPORT when conversion won;
the converted socket's follow-up send used only udp_sendmsg(). No KASAN,
Oops, warning, or panic followed. The concurrent mapped-connect variant
was validated from the same source ordering, but was not forced in a
dedicated runtime interleaving.
Fixes: 03485f2adcde ("udpv6: Add lockless sendmsg() support")
Closes: https://lore.kernel.org/netdev/20260825160630.1888866-1-4ncienth@gmail.com/
Link: https://lore.kernel.org/netdev/a86aa34a-8bea-46d2-a785-6c423ac00b1d@redhat.com/
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Daehyeon Ko <4ncienth@gmail.com>
---
Changes in v3:
- Address Paolo and Sashiko's finding that AF_UNSPEC and a concurrent mapped
connect can publish an IPv6 socket dst before the family revalidation.
- Revalidate after a connected route lookup and reset a dst stored after an
already completed conversion.
- Reset the UDP socket dst after ADDRFORM publishes AF_INET, covering the
opposite store-before-convert ordering and the non-cork path.
- Retain the post-lock family check which prevents late AF_INET6 cork
publication.
- Add route-enabled, zero-capability KASAN matrix and cork/non-cork race
validation. The separate privileged sockmap mismatch remains out of scope.
- State that the concurrent mapped-connect variant was source-validated but
was not forced in a dedicated runtime interleaving.
v2: https://lore.kernel.org/netdev/20260829132125.1160893-1-4ncienth@gmail.com/
v1: https://lore.kernel.org/netdev/20260825160630.1888866-1-4ncienth@gmail.com/
Testing notes:
- Build and runtime validation used x86_64. Sparse was unavailable;
allmodconfig and multi-architecture builds were not run.
- The tested reproducer is available privately on request and is not included
in this public AI-assisted security-fix submission.
---
net/ipv6/ipv6_sockglue.c | 1 +
net/ipv6/udp.c | 11 +++++++++++
2 files changed, 12 insertions(+)
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index b4c977434c2e..59ca446d3156 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -611,6 +611,7 @@ int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
WRITE_ONCE(sk->sk_prot, &udp_prot);
WRITE_ONCE(sk->sk_socket->ops, &inet_dgram_ops);
WRITE_ONCE(sk->sk_family, PF_INET);
+ sk_dst_reset(sk);
}
/* Disable all options not to allocate memory anymore,
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 93478d1ad576..8ba552f1a6b2 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -1705,6 +1705,12 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
dst = NULL;
goto out;
}
+ if (unlikely(connected &&
+ READ_ONCE(sk->sk_family) != AF_INET6)) {
+ sk_dst_reset(sk);
+ err = -EAFNOSUPPORT;
+ goto out;
+ }
if (ipc6.hlimit < 0)
ipc6.hlimit = ip6_sk_dst_hoplimit(np, fl6, dst);
@@ -1729,6 +1735,11 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
}
lock_sock(sk);
+ if (unlikely(sk->sk_family != AF_INET6)) {
+ release_sock(sk);
+ err = -EAFNOSUPPORT;
+ goto out;
+ }
if (unlikely(up->pending)) {
/* The socket is already corked while preparing it. */
/* ... which is an evident application bug. --ANK */
base-commit: 70f3995830d3f1e79faa14eb0605914f778feca9
--
2.55.0
next reply other threads:[~2026-09-02 1:04 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-02 1:04 Daehyeon Ko [this message]
2026-09-03 13:05 ` [net,v3] udp: revalidate socket family before publishing an IPv6 cork netdev-bot+sashiko
2026-09-03 13:22 ` Paolo Abeni
2026-09-03 21:55 ` Kuniyuki Iwashima
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260902010408.1057857-1-4ncienth@gmail.com \
--to=4ncienth@gmail.com \
--cc=davem@davemloft.net \
--cc=dsahern@kernel.org \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=idosch@nvidia.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=vyasevic@redhat.com \
--cc=willemdebruijn.kernel@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox