From: Yingjie Wang <x-professor@outlook.com>
To: Eric Dumazet <edumazet@kernel.org>, Neal Cardwell <ncardwell@google.com>
Cc: Kuniyuki Iwashima <kuniyu@google.com>,
"David S . Miller" <davem@davemloft.net>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Simon Horman <horms@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
John Fastabend <john.fastabend@gmail.com>,
Stanislav Fomichev <sdf@fomichev.me>,
Martin KaFai Lau <martin.lau@linux.dev>,
Alexei Starovoitov <ast@kernel.org>,
Andrii Nakryiko <andrii@kernel.org>,
Eduard Zingerman <eddyz87@gmail.com>,
Kumar Kartikeya Dwivedi <memxor@gmail.com>,
Song Liu <song@kernel.org>,
Yonghong Song <yonghong.song@linux.dev>,
Jiri Olsa <jolsa@kernel.org>,
Emil Tsalapatis <emil@etsalapatis.com>,
Ihor Solodrai <ihor.solodrai@linux.dev>,
"netdev@vger.kernel.org" <netdev@vger.kernel.org>,
"bpf@vger.kernel.org" <bpf@vger.kernel.org>,
"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
"stable@vger.kernel.org" <stable@vger.kernel.org>
Subject: [PATCH net v3] tcp: free saved_syn after an RCU grace period
Date: Wed, 4 Nov 2026 12:32:19 +0000 [thread overview]
Message-ID: <38fa9f2cc476502e9b1c1fef09ebba1df5dd5901.1793795528.git.x-professor@outlook.com> (raw)
tcp_reqsk_record_syn() allocates saved_syn separately from its request
socket. bpf_sock_ops_get_syn() can load that pointer while processing a
retransmitted SYN under RCU.
There are two ownership transfers which can let another CPU reclaim the
allocation while such a reader is still using it.
First, tcp_move_syn() transfers req->saved_syn to a newly created child
socket. The child can be closed and tcp_saved_syn_free() can reclaim the
allocation before a reader of the old request finishes.
Second, inet_reqsk_clone() shallow-copies request_sock during request
migration. After reqsk_migrate_reset() clears the old request pointer, the
migrated clone owns it. However, a reader which already loaded the pointer
from the old request can still be using it when the clone is released.
I found this through LLM-assisted concurrency fuzzing.
Mark the request-socket saved_syn pointer as RCU protected and use RCU
accessors for its reads and updates. Keep the full-socket pointer plain
because all of its accesses hold the socket lock. Embed an rcu_head in
struct saved_syn and use kfree_rcu() in both teardown paths. This lets a
request-side reader which loaded the object before an ownership transfer
finish.
This follows commit e3f33b0a1d89 ("ipv4: free inet_opt and ireq_opt
after an RCU grace period"), which fixed the same ownership-transfer
pattern for IPv4 options.
The issue was confirmed with KASAN on v7.3-16007-g36aad21d50d1 using
temporary kernel instrumentation to widen the reader and request-migration
window. KASAN reported a slab-use-after-free read in
bpf_sock_ops_get_syn(), with the allocation freed by __reqsk_free() from
reqsk_timer_handler(). The same explicit migration test completed without
a report with deferred reclamation. A patched kernel without the delay
instrumentation also completed a successful migration and seven saved-SYN
reads without a KASAN report.
Fixes: 0813a841566f ("bpf: tcp: Allow bpf prog to write and parse TCP header option")
Fixes: c905dee62232 ("tcp: Migrate TCP_NEW_SYN_RECV requests at retransmitting SYN+ACKs.")
Cc: stable@vger.kernel.org
Signed-off-by: Yingjie Wang <x-professor@outlook.com>
---
Changes in v3:
- Keep tcp_sock::saved_syn under the socket lock and apply RCU protection
only to request_sock::saved_syn, as suggested by Eric Dumazet.
- Use unrcu_pointer() when transferring or reclaiming the request-side
pointer.
- Describe the discovery as LLM-assisted concurrency fuzzing.
- Rebase onto net/main commit 241dec6f9ab7.
v2: https://lore.kernel.org/r/3dd45a687afdd0213ee82685c52777e5b0855e20.1793704772.git.x-professor@outlook.com
- Add RCU annotations and accessors following Kuniyuki Iwashima's review.
v1: https://lore.kernel.org/r/34f32e63b803f9f7faf0b0a5754219e48a986136.1793616950.git.x-professor@outlook.com
include/linux/tcp.h | 6 +++---
include/net/request_sock.h | 4 +++-
net/core/filter.c | 2 +-
net/ipv4/inet_connection_sock.c | 6 +++---
net/ipv4/tcp_input.c | 2 +-
5 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/include/linux/tcp.h b/include/linux/tcp.h
index d18f7bc1e5d0..3ae57ae65ec4 100644
--- a/include/linux/tcp.h
+++ b/include/linux/tcp.h
@@ -618,13 +618,13 @@ static inline void fastopen_queue_tune(struct sock *sk, int backlog)
static inline void tcp_move_syn(struct tcp_sock *tp,
struct request_sock *req)
{
- tp->saved_syn = req->saved_syn;
- req->saved_syn = NULL;
+ tp->saved_syn = unrcu_pointer(req->saved_syn);
+ RCU_INIT_POINTER(req->saved_syn, NULL);
}
static inline void tcp_saved_syn_free(struct tcp_sock *tp)
{
- kfree(tp->saved_syn);
+ kfree_rcu(tp->saved_syn, rcu);
tp->saved_syn = NULL;
}
diff --git a/include/net/request_sock.h b/include/net/request_sock.h
index 5a9c826a7092..ddc134f1d15d 100644
--- a/include/net/request_sock.h
+++ b/include/net/request_sock.h
@@ -16,6 +16,7 @@
#include <linux/types.h>
#include <linux/bug.h>
#include <linux/refcount.h>
+#include <linux/rcupdate.h>
#include <net/sock.h>
#include <net/rstreason.h>
@@ -39,6 +40,7 @@ struct request_sock_ops {
};
struct saved_syn {
+ struct rcu_head rcu;
u32 mac_hdrlen;
u32 network_hdrlen;
u32 tcp_hdrlen;
@@ -68,7 +70,7 @@ struct request_sock {
struct timer_list rsk_timer;
const struct request_sock_ops *rsk_ops;
struct sock *sk;
- struct saved_syn *saved_syn;
+ struct saved_syn __rcu *saved_syn;
u32 secid;
u32 peer_secid;
u32 timeout;
diff --git a/net/core/filter.c b/net/core/filter.c
index d832d3332c6f..c5decaba928e 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -6125,7 +6125,7 @@ static int bpf_sock_ops_get_syn(struct bpf_sock_ops_kern *bpf_sock,
* not be available. It has to resort to
* saved_syn (if it is saved).
*/
- saved_syn = inet_reqsk(sk)->saved_syn;
+ saved_syn = rcu_dereference(inet_reqsk(sk)->saved_syn);
else
saved_syn = tcp_sk(sk)->saved_syn;
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index e56beec43877..37cc8606d683 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -869,7 +869,7 @@ reqsk_alloc_noprof(const struct request_sock_ops *ops, struct sock *sk_listener,
req_to_sk(req)->sk_prot = sk_listener->sk_prot;
sk_node_init(&req_to_sk(req)->sk_node);
sk_tx_queue_clear(req_to_sk(req));
- req->saved_syn = NULL;
+ RCU_INIT_POINTER(req->saved_syn, NULL);
req->syncookie = 0;
req->num_timeout = 0;
req->num_retrans = 0;
@@ -908,7 +908,7 @@ void __reqsk_free(struct request_sock *req)
req->rsk_ops->destructor(req);
if (req->rsk_listener)
sock_put(req->rsk_listener);
- kfree(req->saved_syn);
+ kfree_rcu(unrcu_pointer(req->saved_syn), rcu);
kmem_cache_free(req->rsk_ops->slab, req);
}
@@ -979,7 +979,7 @@ static void reqsk_queue_migrated(struct request_sock_queue *queue,
static void reqsk_migrate_reset(struct request_sock *req)
{
- req->saved_syn = NULL;
+ RCU_INIT_POINTER(req->saved_syn, NULL);
#if IS_ENABLED(CONFIG_IPV6)
inet_rsk(req)->ipv6_opt = NULL;
inet_rsk(req)->pktopts = NULL;
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 73c5b4189866..4e87cddad6d9 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -7638,7 +7638,7 @@ static void tcp_reqsk_record_syn(const struct sock *sk,
saved_syn->network_hdrlen = skb_network_header_len(skb);
saved_syn->tcp_hdrlen = tcp_hdrlen(skb);
memcpy(saved_syn->data, base, len);
- req->saved_syn = saved_syn;
+ RCU_INIT_POINTER(req->saved_syn, saved_syn);
}
}
}
base-commit: 241dec6f9ab74d8c3ef246764f3611666b96b9e1
--
2.43.0
next reply other threads:[~2026-11-04 12:32 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-04 12:32 Yingjie Wang [this message]
2026-11-04 12:40 ` Eric Dumazet
2026-11-05 20:36 ` 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=38fa9f2cc476502e9b1c1fef09ebba1df5dd5901.1793795528.git.x-professor@outlook.com \
--to=x-professor@outlook.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=davem@davemloft.net \
--cc=eddyz87@gmail.com \
--cc=edumazet@kernel.org \
--cc=emil@etsalapatis.com \
--cc=horms@kernel.org \
--cc=ihor.solodrai@linux.dev \
--cc=john.fastabend@gmail.com \
--cc=jolsa@kernel.org \
--cc=kuba@kernel.org \
--cc=kuniyu@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=martin.lau@linux.dev \
--cc=memxor@gmail.com \
--cc=ncardwell@google.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=sdf@fomichev.me \
--cc=song@kernel.org \
--cc=stable@vger.kernel.org \
--cc=yonghong.song@linux.dev \
/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
all inboxes | Powered by JetHome®