summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKyle Zeng <kylebot@openai.com>2026-10-28 16:41:01 +0100
committerJakub Kicinski <kuba@kernel.org>2026-10-29 09:06:24 -0700
commitb7ff0e7a9ae3df2f123807a7e670bc348b1e1b9d (patch)
tree212f0d61bae3846c3801f44a5e4b601c871a8d55
parentc1df9c0c0536393de43310f84ca4f8b217271a00 (diff)
downloadlinux-b7ff0e7a9ae3df2f123807a7e670bc348b1e1b9d.tar.gz
linux-b7ff0e7a9ae3df2f123807a7e670bc348b1e1b9d.zip
tcp: initialize mapped children before route lookup
IPv4 passive opens on a dual-stack listener go through tcp_v4_syn_recv_sock() with tcp_v6_mapped_child_init() as the optional initializer. The callback currently runs after the output route lookup. If that lookup fails, tcp_done() destroys a clone whose pinet6 and ipv6_fl_list still refer to the listener's state. With the flowlabel list head in inet_sock, fl6_free_socklist() walks the clone's list and RCU-frees its nodes without unlinking them from the live listener. Subsequent flowlabel operations can use or free the nodes again. Commit 858d2a4f67ff ("tcp: fix potential race in tcp_v6_syn_recv_sock()") moved the initializer before ehash insertion, but did not cover this error path. Invoke the existing initializer after the IPv4 fields it consumes have been set, but before route lookup can fail. It needs neither the destination nor sk_setup_caps(), and all later disposal paths remain covered. Fixes: 1c17f4373d4d ("ipv6: Move ipv6_fl_list from ipv6_pinfo to inet_sock.") Signed-off-by: Kyle Zeng <kylebot@openai.com> Signed-off-by: Eric Dumazet <edumazet@kernel.org> Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com> Link: https://patch.msgid.link/20261028154101.2153360-1-edumazet@kernel.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-rw-r--r--net/ipv4/tcp_ipv4.c10
1 files changed, 6 insertions, 4 deletions
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index ebe5aa70374a..a7f9242f1e42 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1723,6 +1723,12 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
if (READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_reflect_tos))
newinet->tos = tcp_rsk(req)->syn_tos & ~INET_ECN_MASK;
+#if IS_ENABLED(CONFIG_IPV6)
+ /* Detach IPv6 state before a route lookup can fail. */
+ if (opt_child_init)
+ opt_child_init(newsk, sk);
+#endif
+
if (!dst) {
dst = inet_csk_route_child_sock(sk, newsk, req);
if (!dst)
@@ -1732,10 +1738,6 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
}
sk_setup_caps(newsk, dst);
-#if IS_ENABLED(CONFIG_IPV6)
- if (opt_child_init)
- opt_child_init(newsk, sk);
-#endif
tcp_ca_openreq_child(newsk, dst);
tcp_sync_mss(newsk, dst4_mtu(dst));