]> git.ipfire.org Git - thirdparty/kernel/stable.git/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)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 18 Apr 2026 08:35:55 +0000 (10:35 +0200)
commit 9b55b253907e7431210483519c5ad711a37dafa1 upstream.

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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
net/mptcp/protocol.c
net/mptcp/protocol.h
net/mptcp/subflow.c

index 5274b19a5dbd76f8a195821656e039ebaa815b27..965819ddc04c952858b6e2a6b291c2c32b9af510 100644 (file)
@@ -4264,6 +4264,8 @@ int __init mptcp_proto_v6_init(void)
 {
        int err;
 
+       mptcp_subflow_v6_init();
+
        mptcp_v6_prot = mptcp_prot;
        strcpy(mptcp_v6_prot.name, "MPTCPv6");
        mptcp_v6_prot.slab = NULL;
index 329071f6b9e174ab85b75bbf9044cd3a0aac8356..fb3e6ac71bd36f082a2da4cbe3512244ab357d30 100644 (file)
@@ -677,6 +677,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 f9ebcfa1acad6d1194f77c9a5bd671183dd6a218..4377d4058ee61915ef2f3928353ec087a159ca4d 100644 (file)
@@ -1993,7 +1993,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
@@ -2032,10 +2040,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