[PATCH net] mptcp: hold msk reference when cloning request sockets
David Lee <[email protected]> Tue, 4 Aug 2026 09:50:49 +0000
| Newsgroups | dev.linux.lists.mptcp,org.kernel.vger.linux-kernel,org.kernel.vger.netdev,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
From: Kyle Zeng <[email protected]> An MP_JOIN request owns the reference stored in subflow_req->msk. inet_reqsk_clone() byte-copies that pointer when migrating a request, but does not acquire a reference for the clone. The original and cloned request destructors can consequently drop the same reference, leaving one request with a dangling msk pointer. Let cloned MPTCP requests take their own msk reference. The source request still owns its reference while it is being cloned, so sock_hold() is safe. The clone's normal destructor balances the new reference on both successful and failed migration paths. Fixes: c905dee62232 ("tcp: Migrate TCP_NEW_SYN_RECV requests at retransmitting SYN+ACKs.") Cc: [email protected] Assisted-by: Codex:gpt-5.6-sol Codex:gpt-5.5-cyber Signed-off-by: Kyle Zeng <[email protected]> Co-developed-by: David Lee <[email protected]> Signed-off-by: David Lee <[email protected]> --- Bug found and triaged by OpenAI Security Research and validated by Trail of Bits. Trail of Bits has a reproducer for this bug that triggers a KASAN use-after-free and can share if needed. include/net/mptcp.h | 5 +++++ net/ipv4/inet_connection_sock.c | 10 ++++++++-- net/mptcp/subflow.c | 8 ++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/include/net/mptcp.h b/include/net/mptcp.h index 71b9fc5a5..51d4b02f5 100644 --- a/include/net/mptcp.h +++ b/include/net/mptcp.h @@ -223,6 +223,7 @@ int mptcp_subflow_init_cookie_req(struct request_sock *req, struct request_sock *mptcp_subflow_reqsk_alloc(const struct request_sock_ops *ops, struct sock *sk_listener, bool attach_listener); +void mptcp_subflow_reqsk_clone(struct request_sock *req); __be32 mptcp_get_reset_option(const struct sk_buff *skb); @@ -309,6 +310,10 @@ static inline struct request_sock *mptcp_subflow_reqsk_alloc(const struct reques return NULL; } +static inline void mptcp_subflow_reqsk_clone(struct request_sock *req) +{ +} + static inline __be32 mptcp_reset_option(const struct sk_buff *skb) { return htonl(0u); } static inline void mptcp_active_detect_blackhole(struct sock *sk, bool expired) { } diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 56902bba5..f3ef9c15a 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -17,6 +17,7 @@ #include <net/inet_timewait_sock.h> #include <net/ip.h> #include <net/route.h> +#include <net/mptcp.h> #include <net/tcp_states.h> #include <net/xfrm.h> #include <net/tcp.h> @@ -946,8 +947,13 @@ static struct request_sock *inet_reqsk_clone(struct request_sock *req, /* We need not acquire fastopenq->lock * because the child socket is locked in inet_csk_listen_stop(). */ - if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(nreq)->tfo_listener) - rcu_assign_pointer(tcp_sk(nreq->sk)->fastopen_rsk, nreq); + if (sk->sk_protocol == IPPROTO_TCP) { + if (tcp_rsk(nreq)->tfo_listener) + rcu_assign_pointer(tcp_sk(nreq->sk)->fastopen_rsk, nreq); + + if (rsk_is_mptcp(req)) + mptcp_subflow_reqsk_clone(nreq); + } return nreq; } diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 8e386899c..1b6aa5e8c 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -47,6 +47,14 @@ static void subflow_req_destructor(struct request_sock *req) mptcp_token_destroy_request(req); } +void mptcp_subflow_reqsk_clone(struct request_sock *req) +{ + struct mptcp_subflow_request_sock *subflow_req = mptcp_subflow_rsk(req); + + if (subflow_req->msk) + sock_hold((struct sock *)subflow_req->msk); +} + static void subflow_generate_hmac(u64 key1, u64 key2, u32 nonce1, u32 nonce2, void *hmac) { -- 2.47.3