From: Florian Fainelli <florian.fainelli@broadcom.com>
To: stable@vger.kernel.org
Cc: Eric Dumazet <edumazet@google.com>,
syzbot+937b5bbb6a815b3e5d0b@syzkaller.appspotmail.com,
Kuniyuki Iwashima <kuniyu@google.com>,
Jakub Kicinski <kuba@kernel.org>,
Florian Fainelli <florian.fainelli@broadcom.com>,
"David S. Miller" <davem@davemloft.net>,
Paolo Abeni <pabeni@redhat.com>, Simon Horman <horms@kernel.org>,
Neal Cardwell <ncardwell@google.com>,
Matthieu Baerts <matttbe@kernel.org>,
Mat Martineau <martineau@kernel.org>,
Geliang Tang <geliang@kernel.org>,
netdev@vger.kernel.org (open list:NETWORKING [GENERAL]),
linux-kernel@vger.kernel.org (open list),
mptcp@lists.linux.dev (open list:NETWORKING [MPTCP]),
bcm-kernel-feedback-list@broadcom.com, atul.joshi@nokia.com,
joakim.tjernlund@nokia.com
Subject: [PATCH stable 5.15 v2] tcp: fix potential race in tcp_v6_syn_recv_sock()
Date: Fri, 4 Sep 2026 11:34:57 -0700 [thread overview]
Message-ID: <20260904183500.3841094-2-florian.fainelli@broadcom.com> (raw)
In-Reply-To: <20260904183500.3841094-1-florian.fainelli@broadcom.com>
From: Eric Dumazet <edumazet@google.com>
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>
Link: https://patch.msgid.link/20260217161205.2079883-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
(cherry picked from commit 858d2a4f67ff69e645a43487ef7ea7f28f06deae)
[florian:
- net/ipv6/tcp_ipv6.c:
- Set `newnp->ipv6_fl_list = NULL` instead of `newinet->ipv6_fl_list = NULL`,
as `ipv6_fl_list` is in `struct ipv6_pinfo`.
- Guarded `af_specific` assignment with `#ifdef CONFIG_TCP_MD5SIG` instead
of checking `CONFIG_TCP_AO`.
- Used `if (tcp_inet6_sk(sk)->repflow)` instead of `inet6_test_bit(REPFLOW, sk)`.
- net/dccp/:
- Updated `syn_recv_sock` implementations and call sites for DCCP, which was
removed in upstream kernels prior to this commit.]
Assisted-by: Cursor:gemini-3.7-flash
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
---
Changes in v2:
- Updated the DCCP subsystem (net/dccp/) to handle the additional
opt_child_init argument in syn_recv_sock callback, fixing build
failures when CONFIG_IP_DCCP is enabled.
include/net/inet_connection_sock.h | 4 +-
include/net/tcp.h | 4 +-
net/dccp/dccp.h | 4 +-
net/dccp/ipv4.c | 4 +-
net/dccp/ipv6.c | 7 ++-
net/dccp/minisocks.c | 2 +-
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 +-
13 files changed, 78 insertions(+), 71 deletions(-)
diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h
index 7a015055fe6d..97e8969b3796 100644
--- a/include/net/inet_connection_sock.h
+++ b/include/net/inet_connection_sock.h
@@ -41,7 +41,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;
u16 net_frag_header_len;
u16 sockaddr_len;
diff --git a/include/net/tcp.h b/include/net/tcp.h
index be91d81d66ab..dc532cbde05e 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -446,7 +446,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 *uaddr, int addr_len);
int tcp_connect(struct sock *sk);
diff --git a/net/dccp/dccp.h b/net/dccp/dccp.h
index 0218eb169891..f9744455113f 100644
--- a/net/dccp/dccp.h
+++ b/net/dccp/dccp.h
@@ -272,7 +272,9 @@ struct sock *dccp_v4_request_recv_sock(const struct sock *sk, struct sk_buff *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 sock *dccp_check_req(struct sock *sk, struct sk_buff *skb,
struct request_sock *req);
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index aaef9557d942..37bd45c11c8e 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -404,7 +404,9 @@ struct sock *dccp_v4_request_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 inet_request_sock *ireq;
struct inet_sock *newinet;
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 26d28e299f56..572b95da0309 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -416,7 +416,9 @@ static struct sock *dccp_v6_request_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 inet_request_sock *ireq = inet_rsk(req);
struct ipv6_pinfo *newnp;
@@ -431,7 +433,8 @@ static struct sock *dccp_v6_request_recv_sock(const struct sock *sk,
* v6 mapped
*/
newsk = dccp_v4_request_recv_sock(sk, skb, req, dst,
- req_unhash, own_req);
+ req_unhash, own_req,
+ opt_child_init);
if (newsk == NULL)
return NULL;
diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c
index 91e7a2202697..6ea298259b03 100644
--- a/net/dccp/minisocks.c
+++ b/net/dccp/minisocks.c
@@ -192,7 +192,7 @@ struct sock *dccp_check_req(struct sock *sk, struct sk_buff *skb,
goto drop;
child = inet_csk(sk)->icsk_af_ops->syn_recv_sock(sk, skb, req, NULL,
- req, &own_req);
+ req, &own_req, NULL);
if (child) {
child = inet_csk_complete_hashdance(sk, child, req, own_req);
goto out;
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 03b0505178f9..5a61f9a364ad 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -201,7 +201,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);
tcp_sk(child)->tsoffset = tsoff;
diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c
index 1c50867d3e7e..f3ed7d5ff7ef 100644
--- a/net/ipv4/tcp_fastopen.c
+++ b/net/ipv4/tcp_fastopen.c
@@ -251,7 +251,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 f7693166c516..32b55a6b5e03 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1556,7 +1556,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;
@@ -1612,6 +1614,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, dst_mtu(dst));
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 061225d645e6..be4fb0ce2574 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -769,7 +769,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 dea59b123eb7..77069176137d 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1234,11 +1234,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);
+
+ 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;
+#ifdef CONFIG_TCP_MD5SIG
+ tcp_sk(newsk)->af_specific = &tcp_sock_ipv6_mapped_specific;
+#endif
+
+ newnp->ipv6_mc_list = NULL;
+ newnp->ipv6_ac_list = NULL;
+ newnp->ipv6_fl_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 (tcp_inet6_sk(sk)->repflow)
+ 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))
{
struct inet_request_sock *ireq;
struct ipv6_pinfo *newnp;
@@ -1254,61 +1291,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;
-
- inet_sk(newsk)->pinet6 = tcp_inet6_sk(newsk);
-
- newinet = inet_sk(newsk);
- 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;
-#ifdef CONFIG_TCP_MD5SIG
- newtp->af_specific = &tcp_sock_ipv6_mapped_specific;
-#endif
-
- newnp->ipv6_mc_list = NULL;
- newnp->ipv6_ac_list = NULL;
- newnp->ipv6_fl_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 (np->repflow)
- 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 2ec22660ba7a..33b16ea7ae5b 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -670,7 +670,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;
@@ -720,7 +722,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 fe014abcfa4f..1fa44d64958c 100644
--- a/net/smc/af_smc.c
+++ b/net/smc/af_smc.c
@@ -76,7 +76,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;
@@ -101,7 +103,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.34.1
next prev parent reply other threads:[~2026-09-04 18:35 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-04 18:34 [PATCH stable 5.10 v2] tcp: fix potential race in tcp_v6_syn_recv_sock() Florian Fainelli
2026-09-04 18:34 ` Florian Fainelli [this message]
2026-09-04 18:34 ` [PATCH stable 6.1 " Florian Fainelli
2026-09-04 18:34 ` [PATCH stable 6.12 " Florian Fainelli
2026-09-04 18:35 ` [PATCH stable 6.6 " Florian Fainelli
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=20260904183500.3841094-2-florian.fainelli@broadcom.com \
--to=florian.fainelli@broadcom.com \
--cc=atul.joshi@nokia.com \
--cc=bcm-kernel-feedback-list@broadcom.com \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=geliang@kernel.org \
--cc=horms@kernel.org \
--cc=joakim.tjernlund@nokia.com \
--cc=kuba@kernel.org \
--cc=kuniyu@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=martineau@kernel.org \
--cc=matttbe@kernel.org \
--cc=mptcp@lists.linux.dev \
--cc=ncardwell@google.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=stable@vger.kernel.org \
--cc=syzbot+937b5bbb6a815b3e5d0b@syzkaller.appspotmail.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