]> git.hungrycats.org Git - linux/commitdiff
mptcp: fix slab-use-after-free in __inet_lookup_established
authorJiayuan Chen <jiayuan.chen@linux.dev>
Mon, 6 Apr 2026 03:15:10 +0000 (11:15 +0800)
committerJakub Kicinski <kuba@kernel.org>
Thu, 9 Apr 2026 02:30:08 +0000 (19:30 -0700)
The ehash table lookups are lockless and rely on
SLAB_TYPESAFE_BY_RCU to guarantee socket memory stability
during RCU read-side critical sections. Both tcp_prot and
tcpv6_prot have their slab caches created with this flag
via proto_register().

However, MPTCP's mptcp_subflow_init() copies tcpv6_prot into
tcpv6_prot_override during inet_init() (fs_initcall, level 5),
before inet6_init() (module_init/device_initcall, level 6) has
called proto_register(&tcpv6_prot). At that point,
tcpv6_prot.slab is still NULL, so tcpv6_prot_override.slab
remains NULL permanently.

This causes MPTCP v6 subflow child sockets to be allocated via
kmalloc (falling into kmalloc-4k) instead of the TCPv6 slab
cache. The kmalloc-4k cache lacks SLAB_TYPESAFE_BY_RCU, so
when these sockets are freed without SOCK_RCU_FREE (which is
cleared for child sockets by design), the memory can be
immediately reused. Concurrent ehash lookups under
rcu_read_lock can then access freed memory, triggering a
slab-use-after-free in __inet_lookup_established.

Fix this by splitting the IPv6-specific initialization out of
mptcp_subflow_init() into a new mptcp_subflow_v6_init(), called
from mptcp_proto_v6_init() before protocol registration. This
ensures tcpv6_prot_override.slab correctly inherits the
SLAB_TYPESAFE_BY_RCU slab cache.

Fixes: b19bc2945b40 ("mptcp: implement delegated actions")
Cc: stable@vger.kernel.org
Signed-off-by: Jiayuan Chen <jiayuan.chen@linux.dev>
Reviewed-by: Matthieu Baerts (NGI0) <matttbe@kernel.org>
Link: https://patch.msgid.link/20260406031512.189159-1-jiayuan.chen@linux.dev
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/mptcp/protocol.c
net/mptcp/protocol.h
net/mptcp/subflow.c

index 65c3bb8016f47edb46b575185445fe58db6451f1..614c3f583ca0305dd3743ccb46c9659507afb49b 100644 (file)
@@ -4660,6 +4660,8 @@ int __init mptcp_proto_v6_init(void)
 {
        int err;
 
+       mptcp_subflow_v6_init();
+
        mptcp_v6_prot = mptcp_prot;
        strscpy(mptcp_v6_prot.name, "MPTCPv6", sizeof(mptcp_v6_prot.name));
        mptcp_v6_prot.slab = NULL;
index 0bd1ee860316dd25e88beb1b9ba020abdc32f7cb..ec15e503da8b70561e0b06d275839526bfbe0686 100644 (file)
@@ -875,6 +875,7 @@ static inline void mptcp_subflow_tcp_fallback(struct sock *sk,
 void __init mptcp_proto_init(void);
 #if IS_ENABLED(CONFIG_MPTCP_IPV6)
 int __init mptcp_proto_v6_init(void);
+void __init mptcp_subflow_v6_init(void);
 #endif
 
 struct sock *mptcp_sk_clone_init(const struct sock *sk,
index 6716970693e9b71c3e620cdca42d591dafa27056..4ff5863aa9fdeba878ad944f9d49e463ebaad2bc 100644 (file)
@@ -2165,7 +2165,15 @@ void __init mptcp_subflow_init(void)
        tcp_prot_override.psock_update_sk_prot = NULL;
 #endif
 
+       mptcp_diag_subflow_init(&subflow_ulp_ops);
+
+       if (tcp_register_ulp(&subflow_ulp_ops) != 0)
+               panic("MPTCP: failed to register subflows to ULP\n");
+}
+
 #if IS_ENABLED(CONFIG_MPTCP_IPV6)
+void __init mptcp_subflow_v6_init(void)
+{
        /* In struct mptcp_subflow_request_sock, we assume the TCP request sock
         * structures for v4 and v6 have the same size. It should not changed in
         * the future but better to make sure to be warned if it is no longer
@@ -2204,10 +2212,5 @@ void __init mptcp_subflow_init(void)
        /* Disable sockmap processing for subflows */
        tcpv6_prot_override.psock_update_sk_prot = NULL;
 #endif
-#endif
-
-       mptcp_diag_subflow_init(&subflow_ulp_ops);
-
-       if (tcp_register_ulp(&subflow_ulp_ops) != 0)
-               panic("MPTCP: failed to register subflows to ULP\n");
 }
+#endif