udpv6_sendmsg() prepares the IPv6 flow and route 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 while the send path is doing that lockless preparation because no cork has been published yet. If the conversion wins the race, udpv6_sendmsg() later publishes an AF_INET6 cork on an AF_INET socket. Uncorking through the IPv4 socket operations then interprets the IPv6 cork as IPv4 state. The IPv4 finalizer writes a 20-byte IPv4 header into the 40-byte IPv6 header 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; one of three fresh boots contained recognizable stale heap data. The route prepared by the racing send is not published in the socket dst cache. An implicit send on the IPv4-mapped peer required by ADDRFORM delegates to udp_sendmsg() before the IPv6 lookup. An explicit native IPv6 destination reaches the lookup, but leaves connected false and therefore cannot call ip6_sk_dst_store_flow(). Conversely, a native connected send can publish an IPv6 dst, but ADDRFORM rejects that peer with EADDRNOTAVAIL. The same reasoning covers the non-corking explicit send path. After taking the lock, revalidate that IPV6_ADDRFORM has not changed the socket family before publishing the cork. The existing error path releases the invocation's prepared dst, flowlabel, and transmit-option references; there is no socket dst cache entry from this send to reset. With this change, the serialized controls retain their existing results and the forbidden mixed state occurred zero times across 20,000 sockets. Fixes: 03485f2adcde ("udpv6: Add lockless sendmsg() support") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Daehyeon Ko <4ncienth@gmail.com> --- Changes in v2: - Add the requested individual maintainer Cc recipients. - Explain why the corked and non-corked explicit IPv6 sends do not publish an IPv6 socket dst cache, and why sk_dst_reset() is not needed here. - Note that the separately raised UDP sockmap proto mismatch was reproduced and will be handled independently. - No code changes. --- net/ipv6/udp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index fd875908ac0c66..566c634a5a5945 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1716,6 +1716,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: 2188569e7e1b0bc3f3b557dc97ab7a02befc11c8 -- 2.54.0