public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH net] tcp: fix potential race in tcp_v6_syn_recv_sock()
@ 2026-02-17 16:12 Eric Dumazet
  2026-02-17 20:41 ` Kuniyuki Iwashima
  2026-02-19 22:30 ` patchwork-bot+netdevbpf
  0 siblings, 2 replies; 3+ messages in thread
From: Eric Dumazet @ 2026-02-17 16:12 UTC (permalink / raw)
  To: David S . Miller, Jakub Kicinski, Paolo Abeni
  Cc: Simon Horman, Neal Cardwell, Kuniyuki Iwashima, Willem de Bruijn,
	netdev, eric.dumazet, Eric Dumazet, syzbot+937b5bbb6a815b3e5d0b

Code in tcp_v6_syn_recv_sock() after the call to tcp_v4_syn_recv_sock()
is done too late.

After tcp_v4_syn_recv_sock(), the child socket is already visible
from TCP ehash table and other cpus might use it.

Since newinet->pinet6 is still pointing to the listener ipv6_pinfo
bad things can happen as syzbot found.

Move the problematic code in tcp_v6_mapped_child_init()
and call this new helper from tcp_v4_syn_recv_sock() before
the ehash insertion.

This allows the removal of one tcp_sync_mss(), since
tcp_v4_syn_recv_sock() will call it with the correct
context.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: syzbot+937b5bbb6a815b3e5d0b@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/netdev/69949275.050a0220.2eeac1.0145.GAE@google.com/
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
 include/net/inet_connection_sock.h |  4 +-
 include/net/tcp.h                  |  4 +-
 net/ipv4/syncookies.c              |  2 +-
 net/ipv4/tcp_fastopen.c            |  2 +-
 net/ipv4/tcp_ipv4.c                |  8 ++-
 net/ipv4/tcp_minisocks.c           |  2 +-
 net/ipv6/tcp_ipv6.c                | 98 +++++++++++++-----------------
 net/mptcp/subflow.c                |  6 +-
 net/smc/af_smc.c                   |  6 +-
 9 files changed, 66 insertions(+), 66 deletions(-)

diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h
index ecb362025c4e5183ec78aef4b45c249da87c19ea..5cb3056d6ddc7cb9489131f503b32f3dac211bec 100644
--- a/include/net/inet_connection_sock.h
+++ b/include/net/inet_connection_sock.h
@@ -42,7 +42,9 @@ struct inet_connection_sock_af_ops {
 				      struct request_sock *req,
 				      struct dst_entry *dst,
 				      struct request_sock *req_unhash,
-				      bool *own_req);
+				      bool *own_req,
+				      void (*opt_child_init)(struct sock *newsk,
+							     const struct sock *sk));
 	u16	    net_header_len;
 	int	    (*setsockopt)(struct sock *sk, int level, int optname,
 				  sockptr_t optval, unsigned int optlen);
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 40e72b9cb85f08714d3f458c0bd1402a5fb1eb4e..eb8bf63fdafc3243469f293fd06aef0ce086c5a4 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -544,7 +544,9 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
 				  struct request_sock *req,
 				  struct dst_entry *dst,
 				  struct request_sock *req_unhash,
-				  bool *own_req);
+				  bool *own_req,
+				  void (*opt_child_init)(struct sock *newsk,
+							 const struct sock *sk));
 int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb);
 int tcp_v4_connect(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len);
 int tcp_connect(struct sock *sk);
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 569befcf021ba5598e57ee3256843c141aeb412b..061751aabc8e16c5d536a19f7b920d1bca2b0f4f 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -203,7 +203,7 @@ struct sock *tcp_get_cookie_sock(struct sock *sk, struct sk_buff *skb,
 	bool own_req;
 
 	child = icsk->icsk_af_ops->syn_recv_sock(sk, skb, req, dst,
-						 NULL, &own_req);
+						 NULL, &own_req, NULL);
 	if (child) {
 		refcount_set(&req->rsk_refcnt, 1);
 		sock_rps_save_rxhash(child, skb);
diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c
index b30090cff3cf7d925dc46694860abd3ca5516d70..df803f088f454cf157e0efbba8e2ff3f6d205530 100644
--- a/net/ipv4/tcp_fastopen.c
+++ b/net/ipv4/tcp_fastopen.c
@@ -333,7 +333,7 @@ static struct sock *tcp_fastopen_create_child(struct sock *sk,
 	bool own_req;
 
 	child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL,
-							 NULL, &own_req);
+							 NULL, &own_req, NULL);
 	if (!child)
 		return NULL;
 
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 6264fc0b2be53854598d7c37ce1c7928d2e0b507..2980c175d326b6c6ebd4acba0c4333f481617579 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1705,7 +1705,9 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
 				  struct request_sock *req,
 				  struct dst_entry *dst,
 				  struct request_sock *req_unhash,
-				  bool *own_req)
+				  bool *own_req,
+				  void (*opt_child_init)(struct sock *newsk,
+							 const struct sock *sk))
 {
 	struct inet_request_sock *ireq;
 	bool found_dup_sk = false;
@@ -1757,6 +1759,10 @@ 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));
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index ec128865f5c029c971eb00cb9ee058b742efafd1..d9c5a43bd2818c17d79d8433ac5dd2bdd199b812 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -925,7 +925,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
 	 * socket is created, wait for troubles.
 	 */
 	child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL,
-							 req, &own_req);
+							 req, &own_req, NULL);
 	if (!child)
 		goto listen_overflow;
 
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index d10487b4e5bff87d4ff2a7b912a826964101a163..e46a0efae01235ae7430ed268b92cb47309b8d28 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1312,11 +1312,48 @@ static void tcp_v6_restore_cb(struct sk_buff *skb)
 		sizeof(struct inet6_skb_parm));
 }
 
+/* Called from tcp_v4_syn_recv_sock() for v6_mapped children. */
+static void tcp_v6_mapped_child_init(struct sock *newsk, const struct sock *sk)
+{
+	struct inet_sock *newinet = inet_sk(newsk);
+	struct ipv6_pinfo *newnp;
+
+	newinet->pinet6 = newnp = tcp_inet6_sk(newsk);
+	newinet->ipv6_fl_list = NULL;
+
+	memcpy(newnp, tcp_inet6_sk(sk), sizeof(struct ipv6_pinfo));
+
+	newnp->saddr = newsk->sk_v6_rcv_saddr;
+
+	inet_csk(newsk)->icsk_af_ops = &ipv6_mapped;
+	if (sk_is_mptcp(newsk))
+		mptcpv6_handle_mapped(newsk, true);
+	newsk->sk_backlog_rcv = tcp_v4_do_rcv;
+#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
+	tcp_sk(newsk)->af_specific = &tcp_sock_ipv6_mapped_specific;
+#endif
+
+	newnp->ipv6_mc_list = NULL;
+	newnp->ipv6_ac_list = NULL;
+	newnp->pktoptions  = NULL;
+	newnp->opt	   = NULL;
+
+	/* tcp_v4_syn_recv_sock() has initialized newinet->mc_{index,ttl} */
+	newnp->mcast_oif   = newinet->mc_index;
+	newnp->mcast_hops  = newinet->mc_ttl;
+
+	newnp->rcv_flowinfo = 0;
+	if (inet6_test_bit(REPFLOW, sk))
+		newnp->flow_label = 0;
+}
+
 static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
 					 struct request_sock *req,
 					 struct dst_entry *dst,
 					 struct request_sock *req_unhash,
-					 bool *own_req)
+					 bool *own_req,
+					 void (*opt_child_init)(struct sock *newsk,
+								const struct sock *sk))
 {
 	const struct ipv6_pinfo *np = tcp_inet6_sk(sk);
 	struct inet_request_sock *ireq;
@@ -1332,61 +1369,10 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
 #endif
 	struct flowi6 fl6;
 
-	if (skb->protocol == htons(ETH_P_IP)) {
-		/*
-		 *	v6 mapped
-		 */
-
-		newsk = tcp_v4_syn_recv_sock(sk, skb, req, dst,
-					     req_unhash, own_req);
-
-		if (!newsk)
-			return NULL;
-
-		newinet = inet_sk(newsk);
-		newinet->pinet6 = tcp_inet6_sk(newsk);
-		newinet->ipv6_fl_list = NULL;
-
-		newnp = tcp_inet6_sk(newsk);
-		newtp = tcp_sk(newsk);
-
-		memcpy(newnp, np, sizeof(struct ipv6_pinfo));
-
-		newnp->saddr = newsk->sk_v6_rcv_saddr;
-
-		inet_csk(newsk)->icsk_af_ops = &ipv6_mapped;
-		if (sk_is_mptcp(newsk))
-			mptcpv6_handle_mapped(newsk, true);
-		newsk->sk_backlog_rcv = tcp_v4_do_rcv;
-#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
-		newtp->af_specific = &tcp_sock_ipv6_mapped_specific;
-#endif
-
-		newnp->ipv6_mc_list = NULL;
-		newnp->ipv6_ac_list = NULL;
-		newnp->pktoptions  = NULL;
-		newnp->opt	   = NULL;
-		newnp->mcast_oif   = inet_iif(skb);
-		newnp->mcast_hops  = ip_hdr(skb)->ttl;
-		newnp->rcv_flowinfo = 0;
-		if (inet6_test_bit(REPFLOW, sk))
-			newnp->flow_label = 0;
-
-		/*
-		 * No need to charge this sock to the relevant IPv6 refcnt debug socks count
-		 * here, tcp_create_openreq_child now does this for us, see the comment in
-		 * that function for the gory details. -acme
-		 */
-
-		/* It is tricky place. Until this moment IPv4 tcp
-		   worked with IPv6 icsk.icsk_af_ops.
-		   Sync it now.
-		 */
-		tcp_sync_mss(newsk, inet_csk(newsk)->icsk_pmtu_cookie);
-
-		return newsk;
-	}
-
+	if (skb->protocol == htons(ETH_P_IP))
+		return tcp_v4_syn_recv_sock(sk, skb, req, dst,
+					    req_unhash, own_req,
+					    tcp_v6_mapped_child_init);
 	ireq = inet_rsk(req);
 
 	if (sk_acceptq_is_full(sk))
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index f66129f1e649248e00215d254c925f66c3fffa6a..8b25dd86ffeb668b98fd41026fb084dab7dc23b8 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -808,7 +808,9 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk,
 					  struct request_sock *req,
 					  struct dst_entry *dst,
 					  struct request_sock *req_unhash,
-					  bool *own_req)
+					  bool *own_req,
+					  void (*opt_child_init)(struct sock *newsk,
+								 const struct sock *sk))
 {
 	struct mptcp_subflow_context *listener = mptcp_subflow_ctx(sk);
 	struct mptcp_subflow_request_sock *subflow_req;
@@ -855,7 +857,7 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk,
 
 create_child:
 	child = listener->icsk_af_ops->syn_recv_sock(sk, skb, req, dst,
-						     req_unhash, own_req);
+						     req_unhash, own_req, opt_child_init);
 
 	if (child && *own_req) {
 		struct mptcp_subflow_context *ctx = mptcp_subflow_ctx(child);
diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c
index d8201eb3ac5f3d87a15f385862c4ae82ae3a24ff..18c56b0d7ad531e575266ad7f38187973422151f 100644
--- a/net/smc/af_smc.c
+++ b/net/smc/af_smc.c
@@ -124,7 +124,9 @@ static struct sock *smc_tcp_syn_recv_sock(const struct sock *sk,
 					  struct request_sock *req,
 					  struct dst_entry *dst,
 					  struct request_sock *req_unhash,
-					  bool *own_req)
+					  bool *own_req,
+					  void (*opt_child_init)(struct sock *newsk,
+								 const struct sock *sk))
 {
 	struct smc_sock *smc;
 	struct sock *child;
@@ -142,7 +144,7 @@ static struct sock *smc_tcp_syn_recv_sock(const struct sock *sk,
 
 	/* passthrough to original syn recv sock fct */
 	child = smc->ori_af_ops->syn_recv_sock(sk, skb, req, dst, req_unhash,
-					       own_req);
+					       own_req, opt_child_init);
 	/* child must not inherit smc or its ops */
 	if (child) {
 		rcu_assign_sk_user_data(child, NULL);
-- 
2.53.0.310.g728cabbaf7-goog


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH net] tcp: fix potential race in tcp_v6_syn_recv_sock()
  2026-02-17 16:12 [PATCH net] tcp: fix potential race in tcp_v6_syn_recv_sock() Eric Dumazet
@ 2026-02-17 20:41 ` Kuniyuki Iwashima
  2026-02-19 22:30 ` patchwork-bot+netdevbpf
  1 sibling, 0 replies; 3+ messages in thread
From: Kuniyuki Iwashima @ 2026-02-17 20:41 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Neal Cardwell, Willem de Bruijn, netdev, eric.dumazet,
	syzbot+937b5bbb6a815b3e5d0b

On Tue, Feb 17, 2026 at 8:12 AM Eric Dumazet <edumazet@google.com> wrote:
>
> Code in tcp_v6_syn_recv_sock() after the call to tcp_v4_syn_recv_sock()
> is done too late.
>
> After tcp_v4_syn_recv_sock(), the child socket is already visible
> from TCP ehash table and other cpus might use it.
>
> Since newinet->pinet6 is still pointing to the listener ipv6_pinfo
> bad things can happen as syzbot found.
>
> Move the problematic code in tcp_v6_mapped_child_init()
> and call this new helper from tcp_v4_syn_recv_sock() before
> the ehash insertion.
>
> This allows the removal of one tcp_sync_mss(), since
> tcp_v4_syn_recv_sock() will call it with the correct
> context.
>
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Reported-by: syzbot+937b5bbb6a815b3e5d0b@syzkaller.appspotmail.com
> Closes: https://lore.kernel.org/netdev/69949275.050a0220.2eeac1.0145.GAE@google.com/
> Signed-off-by: Eric Dumazet <edumazet@google.com>

Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>

Not sure why syzbot failed to decode the line number, but
for those who look for the racy place, INET_ECN_dontxmit()
called from tcp_assign_congestion_control() uses inet6_sk(sk)
of the parent listener.


> ---
>  include/net/inet_connection_sock.h |  4 +-
>  include/net/tcp.h                  |  4 +-
>  net/ipv4/syncookies.c              |  2 +-
>  net/ipv4/tcp_fastopen.c            |  2 +-
>  net/ipv4/tcp_ipv4.c                |  8 ++-
>  net/ipv4/tcp_minisocks.c           |  2 +-
>  net/ipv6/tcp_ipv6.c                | 98 +++++++++++++-----------------
>  net/mptcp/subflow.c                |  6 +-
>  net/smc/af_smc.c                   |  6 +-
>  9 files changed, 66 insertions(+), 66 deletions(-)
>
> diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h
> index ecb362025c4e5183ec78aef4b45c249da87c19ea..5cb3056d6ddc7cb9489131f503b32f3dac211bec 100644
> --- a/include/net/inet_connection_sock.h
> +++ b/include/net/inet_connection_sock.h
> @@ -42,7 +42,9 @@ struct inet_connection_sock_af_ops {
>                                       struct request_sock *req,
>                                       struct dst_entry *dst,
>                                       struct request_sock *req_unhash,
> -                                     bool *own_req);
> +                                     bool *own_req,
> +                                     void (*opt_child_init)(struct sock *newsk,
> +                                                            const struct sock *sk));
>         u16         net_header_len;
>         int         (*setsockopt)(struct sock *sk, int level, int optname,
>                                   sockptr_t optval, unsigned int optlen);
> diff --git a/include/net/tcp.h b/include/net/tcp.h
> index 40e72b9cb85f08714d3f458c0bd1402a5fb1eb4e..eb8bf63fdafc3243469f293fd06aef0ce086c5a4 100644
> --- a/include/net/tcp.h
> +++ b/include/net/tcp.h
> @@ -544,7 +544,9 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
>                                   struct request_sock *req,
>                                   struct dst_entry *dst,
>                                   struct request_sock *req_unhash,
> -                                 bool *own_req);
> +                                 bool *own_req,
> +                                 void (*opt_child_init)(struct sock *newsk,
> +                                                        const struct sock *sk));
>  int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb);
>  int tcp_v4_connect(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len);
>  int tcp_connect(struct sock *sk);
> diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
> index 569befcf021ba5598e57ee3256843c141aeb412b..061751aabc8e16c5d536a19f7b920d1bca2b0f4f 100644
> --- a/net/ipv4/syncookies.c
> +++ b/net/ipv4/syncookies.c
> @@ -203,7 +203,7 @@ struct sock *tcp_get_cookie_sock(struct sock *sk, struct sk_buff *skb,
>         bool own_req;
>
>         child = icsk->icsk_af_ops->syn_recv_sock(sk, skb, req, dst,
> -                                                NULL, &own_req);
> +                                                NULL, &own_req, NULL);
>         if (child) {
>                 refcount_set(&req->rsk_refcnt, 1);
>                 sock_rps_save_rxhash(child, skb);
> diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c
> index b30090cff3cf7d925dc46694860abd3ca5516d70..df803f088f454cf157e0efbba8e2ff3f6d205530 100644
> --- a/net/ipv4/tcp_fastopen.c
> +++ b/net/ipv4/tcp_fastopen.c
> @@ -333,7 +333,7 @@ static struct sock *tcp_fastopen_create_child(struct sock *sk,
>         bool own_req;
>
>         child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL,
> -                                                        NULL, &own_req);
> +                                                        NULL, &own_req, NULL);
>         if (!child)
>                 return NULL;
>
> diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
> index 6264fc0b2be53854598d7c37ce1c7928d2e0b507..2980c175d326b6c6ebd4acba0c4333f481617579 100644
> --- a/net/ipv4/tcp_ipv4.c
> +++ b/net/ipv4/tcp_ipv4.c
> @@ -1705,7 +1705,9 @@ struct sock *tcp_v4_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
>                                   struct request_sock *req,
>                                   struct dst_entry *dst,
>                                   struct request_sock *req_unhash,
> -                                 bool *own_req)
> +                                 bool *own_req,
> +                                 void (*opt_child_init)(struct sock *newsk,
> +                                                        const struct sock *sk))
>  {
>         struct inet_request_sock *ireq;
>         bool found_dup_sk = false;
> @@ -1757,6 +1759,10 @@ 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));
> diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
> index ec128865f5c029c971eb00cb9ee058b742efafd1..d9c5a43bd2818c17d79d8433ac5dd2bdd199b812 100644
> --- a/net/ipv4/tcp_minisocks.c
> +++ b/net/ipv4/tcp_minisocks.c
> @@ -925,7 +925,7 @@ struct sock *tcp_check_req(struct sock *sk, struct sk_buff *skb,
>          * socket is created, wait for troubles.
>          */
>         child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL,
> -                                                        req, &own_req);
> +                                                        req, &own_req, NULL);
>         if (!child)
>                 goto listen_overflow;
>
> diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
> index d10487b4e5bff87d4ff2a7b912a826964101a163..e46a0efae01235ae7430ed268b92cb47309b8d28 100644
> --- a/net/ipv6/tcp_ipv6.c
> +++ b/net/ipv6/tcp_ipv6.c
> @@ -1312,11 +1312,48 @@ static void tcp_v6_restore_cb(struct sk_buff *skb)
>                 sizeof(struct inet6_skb_parm));
>  }
>
> +/* Called from tcp_v4_syn_recv_sock() for v6_mapped children. */
> +static void tcp_v6_mapped_child_init(struct sock *newsk, const struct sock *sk)
> +{
> +       struct inet_sock *newinet = inet_sk(newsk);
> +       struct ipv6_pinfo *newnp;
> +
> +       newinet->pinet6 = newnp = tcp_inet6_sk(newsk);
> +       newinet->ipv6_fl_list = NULL;
> +
> +       memcpy(newnp, tcp_inet6_sk(sk), sizeof(struct ipv6_pinfo));
> +
> +       newnp->saddr = newsk->sk_v6_rcv_saddr;
> +
> +       inet_csk(newsk)->icsk_af_ops = &ipv6_mapped;
> +       if (sk_is_mptcp(newsk))
> +               mptcpv6_handle_mapped(newsk, true);
> +       newsk->sk_backlog_rcv = tcp_v4_do_rcv;
> +#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
> +       tcp_sk(newsk)->af_specific = &tcp_sock_ipv6_mapped_specific;
> +#endif
> +
> +       newnp->ipv6_mc_list = NULL;
> +       newnp->ipv6_ac_list = NULL;
> +       newnp->pktoptions  = NULL;
> +       newnp->opt         = NULL;
> +
> +       /* tcp_v4_syn_recv_sock() has initialized newinet->mc_{index,ttl} */
> +       newnp->mcast_oif   = newinet->mc_index;
> +       newnp->mcast_hops  = newinet->mc_ttl;
> +
> +       newnp->rcv_flowinfo = 0;
> +       if (inet6_test_bit(REPFLOW, sk))
> +               newnp->flow_label = 0;
> +}
> +
>  static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *skb,
>                                          struct request_sock *req,
>                                          struct dst_entry *dst,
>                                          struct request_sock *req_unhash,
> -                                        bool *own_req)
> +                                        bool *own_req,
> +                                        void (*opt_child_init)(struct sock *newsk,
> +                                                               const struct sock *sk))
>  {
>         const struct ipv6_pinfo *np = tcp_inet6_sk(sk);
>         struct inet_request_sock *ireq;
> @@ -1332,61 +1369,10 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
>  #endif
>         struct flowi6 fl6;
>
> -       if (skb->protocol == htons(ETH_P_IP)) {
> -               /*
> -                *      v6 mapped
> -                */
> -
> -               newsk = tcp_v4_syn_recv_sock(sk, skb, req, dst,
> -                                            req_unhash, own_req);
> -
> -               if (!newsk)
> -                       return NULL;
> -
> -               newinet = inet_sk(newsk);
> -               newinet->pinet6 = tcp_inet6_sk(newsk);
> -               newinet->ipv6_fl_list = NULL;
> -
> -               newnp = tcp_inet6_sk(newsk);
> -               newtp = tcp_sk(newsk);
> -
> -               memcpy(newnp, np, sizeof(struct ipv6_pinfo));
> -
> -               newnp->saddr = newsk->sk_v6_rcv_saddr;
> -
> -               inet_csk(newsk)->icsk_af_ops = &ipv6_mapped;
> -               if (sk_is_mptcp(newsk))
> -                       mptcpv6_handle_mapped(newsk, true);
> -               newsk->sk_backlog_rcv = tcp_v4_do_rcv;
> -#if defined(CONFIG_TCP_MD5SIG) || defined(CONFIG_TCP_AO)
> -               newtp->af_specific = &tcp_sock_ipv6_mapped_specific;
> -#endif
> -
> -               newnp->ipv6_mc_list = NULL;
> -               newnp->ipv6_ac_list = NULL;
> -               newnp->pktoptions  = NULL;
> -               newnp->opt         = NULL;
> -               newnp->mcast_oif   = inet_iif(skb);
> -               newnp->mcast_hops  = ip_hdr(skb)->ttl;
> -               newnp->rcv_flowinfo = 0;
> -               if (inet6_test_bit(REPFLOW, sk))
> -                       newnp->flow_label = 0;
> -
> -               /*
> -                * No need to charge this sock to the relevant IPv6 refcnt debug socks count
> -                * here, tcp_create_openreq_child now does this for us, see the comment in
> -                * that function for the gory details. -acme
> -                */
> -
> -               /* It is tricky place. Until this moment IPv4 tcp
> -                  worked with IPv6 icsk.icsk_af_ops.
> -                  Sync it now.
> -                */
> -               tcp_sync_mss(newsk, inet_csk(newsk)->icsk_pmtu_cookie);
> -
> -               return newsk;
> -       }
> -
> +       if (skb->protocol == htons(ETH_P_IP))
> +               return tcp_v4_syn_recv_sock(sk, skb, req, dst,
> +                                           req_unhash, own_req,
> +                                           tcp_v6_mapped_child_init);
>         ireq = inet_rsk(req);
>
>         if (sk_acceptq_is_full(sk))
> diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
> index f66129f1e649248e00215d254c925f66c3fffa6a..8b25dd86ffeb668b98fd41026fb084dab7dc23b8 100644
> --- a/net/mptcp/subflow.c
> +++ b/net/mptcp/subflow.c
> @@ -808,7 +808,9 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk,
>                                           struct request_sock *req,
>                                           struct dst_entry *dst,
>                                           struct request_sock *req_unhash,
> -                                         bool *own_req)
> +                                         bool *own_req,
> +                                         void (*opt_child_init)(struct sock *newsk,
> +                                                                const struct sock *sk))
>  {
>         struct mptcp_subflow_context *listener = mptcp_subflow_ctx(sk);
>         struct mptcp_subflow_request_sock *subflow_req;
> @@ -855,7 +857,7 @@ static struct sock *subflow_syn_recv_sock(const struct sock *sk,
>
>  create_child:
>         child = listener->icsk_af_ops->syn_recv_sock(sk, skb, req, dst,
> -                                                    req_unhash, own_req);
> +                                                    req_unhash, own_req, opt_child_init);
>
>         if (child && *own_req) {
>                 struct mptcp_subflow_context *ctx = mptcp_subflow_ctx(child);
> diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c
> index d8201eb3ac5f3d87a15f385862c4ae82ae3a24ff..18c56b0d7ad531e575266ad7f38187973422151f 100644
> --- a/net/smc/af_smc.c
> +++ b/net/smc/af_smc.c
> @@ -124,7 +124,9 @@ static struct sock *smc_tcp_syn_recv_sock(const struct sock *sk,
>                                           struct request_sock *req,
>                                           struct dst_entry *dst,
>                                           struct request_sock *req_unhash,
> -                                         bool *own_req)
> +                                         bool *own_req,
> +                                         void (*opt_child_init)(struct sock *newsk,
> +                                                                const struct sock *sk))
>  {
>         struct smc_sock *smc;
>         struct sock *child;
> @@ -142,7 +144,7 @@ static struct sock *smc_tcp_syn_recv_sock(const struct sock *sk,
>
>         /* passthrough to original syn recv sock fct */
>         child = smc->ori_af_ops->syn_recv_sock(sk, skb, req, dst, req_unhash,
> -                                              own_req);
> +                                              own_req, opt_child_init);
>         /* child must not inherit smc or its ops */
>         if (child) {
>                 rcu_assign_sk_user_data(child, NULL);
> --
> 2.53.0.310.g728cabbaf7-goog
>

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH net] tcp: fix potential race in tcp_v6_syn_recv_sock()
  2026-02-17 16:12 [PATCH net] tcp: fix potential race in tcp_v6_syn_recv_sock() Eric Dumazet
  2026-02-17 20:41 ` Kuniyuki Iwashima
@ 2026-02-19 22:30 ` patchwork-bot+netdevbpf
  1 sibling, 0 replies; 3+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-02-19 22:30 UTC (permalink / raw)
  To: Eric Dumazet
  Cc: davem, kuba, pabeni, horms, ncardwell, kuniyu, willemb, netdev,
	eric.dumazet, syzbot+937b5bbb6a815b3e5d0b

Hello:

This patch was applied to netdev/net.git (main)
by Jakub Kicinski <kuba@kernel.org>:

On Tue, 17 Feb 2026 16:12:05 +0000 you wrote:
> Code in tcp_v6_syn_recv_sock() after the call to tcp_v4_syn_recv_sock()
> is done too late.
> 
> After tcp_v4_syn_recv_sock(), the child socket is already visible
> from TCP ehash table and other cpus might use it.
> 
> Since newinet->pinet6 is still pointing to the listener ipv6_pinfo
> bad things can happen as syzbot found.
> 
> [...]

Here is the summary with links:
  - [net] tcp: fix potential race in tcp_v6_syn_recv_sock()
    https://git.kernel.org/netdev/net/c/858d2a4f67ff

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-02-19 22:29 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-17 16:12 [PATCH net] tcp: fix potential race in tcp_v6_syn_recv_sock() Eric Dumazet
2026-02-17 20:41 ` Kuniyuki Iwashima
2026-02-19 22:30 ` patchwork-bot+netdevbpf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox