FAILED: patch "[PATCH] mptcp: pm: userspace: fix use-after-free in get_local_id" failed to apply to 6.12-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x 9bc6d5e4ca9f3cbb41d43400b3a31cb0403796c9
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026072919-slouchy-backpack-abaf@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 9bc6d5e4ca9f3cbb41d43400b3a31cb0403796c9 Mon Sep 17 00:00:00 2001
From: Geliang Tang <[email protected]>
Date: Wed, 22 Jul 2026 00:14:39 +0200
Subject: [PATCH] mptcp: pm: userspace: fix use-after-free in get_local_id
In mptcp_pm_userspace_get_local_id(), the address entry is looked up under spinlock, but its id is read after dropping the lock. A concurrent deletion can free the entry between the unlock and the read, leading to UAF.
The race window is narrow. It was reproduced only with a locally constructed stress test that repeatedly overlaps an MP_JOIN SYN with a MPTCP_PM_CMD_SUBFLOW_DESTROY request.
However, the KASAN report below confirms that the race is reachable:
[ 666.319376] BUG: KASAN: slab-use-after-free in mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319386] Read of size 1 at addr ffff888124845610 by task swapper/0/0 ... [ 666.319401] Call Trace: [ 666.319405] <IRQ> [ 666.319408] dump_stack_lvl+0x53/0x70 [ 666.319412] print_address_description.constprop.0+0x2c/0x3b0 [ 666.319418] print_report+0xbe/0x2b0 [ 666.319421] ? mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319423] kasan_report+0xce/0x100 [ 666.319426] ? mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319429] mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319433] mptcp_pm_get_local_id+0x371/0x440 ... [ 666.319821] Allocated by task 45539: [ 666.319844] kasan_save_stack+0x33/0x60 [ 666.319855] kasan_save_track+0x14/0x30 [ 666.319858] __kasan_kmalloc+0x8f/0xa0 [ 666.319863] __kmalloc_noprof+0x1e7/0x520 [ 666.319867] sock_kmalloc+0xdf/0x130 [ 666.319885] sock_kmemdup+0x1b/0x40 [ 666.319888] mptcp_userspace_pm_append_new_local_addr+0x261/0x500 [ 666.319910] mptcp_pm_nl_announce_doit+0x16a/0x610 ... [ 666.319967] Freed by task 45560: [ 666.319988] kasan_save_stack+0x33/0x60 [ 666.319991] kasan_save_track+0x14/0x30 [ 666.319994] kasan_save_free_info+0x3b/0x60 [ 666.319998] __kasan_slab_free+0x43/0x70 [ 666.320000] kfree+0x166/0x440 [ 666.320003] sock_kfree_s+0x1d/0x50 [ 666.320007] mptcp_userspace_pm_delete_local_addr.isra.0+0x157/0x200 [ 666.320011] mptcp_pm_nl_subflow_destroy_doit+0x51d/0xea0
Fix by copying the id into a local variable while still holding the lock, and use -1 as a "not found" sentinel.
net/mptcp/pm_userspace.c +5 -2
@@ -132,12 +132,15 @@ int mptcp_userspace_pm_get_local_id(struct mptcp_sock *msk,__be16 msk_sport = ((struct inet_sock *)inet_sk ((struct sock *)msk))->inet_sport;struct mptcp_pm_addr_entry *entry; +int id;spin_lock_bh (&msk->pm.lock); entry =mptcp_userspace_pm_lookup_addr (msk, &skc->addr); + id = entry ? entry->addr.id :-1 ;spin_unlock_bh (&msk->pm.lock); -if (entry) -return entry->addr.id; + +if (id !=-1 ) +return id;if (skc->addr.port == msk_sport) skc->addr.port =0 ;
Sasha Levin
maintainerPATCH 1/4 mptcp: pm: avoid code duplication to lookup endp
From: Geliang Tang <[email protected]>
[ Upstream commit 1d7fa6ceb91fddbe38cae3521d5d1075bce6a00e ]
The helper __lookup_addr() can be used in mptcp_pm_nl_get_local_id() and mptcp_pm_nl_is_backup() to simplify the code, and avoid code duplication.
Stable-dep-of: 9bc6d5e4ca9f ("mptcp: pm: userspace: fix use-after-free in get_local_id")
--- net/mptcp/pm_netlink.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-)
net/mptcp/pm_netlink.c +6 -14
@@ -1278,17 +1278,13 @@ int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct mptcp_addr_info *skc {struct mptcp_pm_addr_entry *entry;struct pm_nl_pernet *pernet; -int ret =-1 ; +int ret; pernet =pm_nl_get_pernet_from_msk (msk);rcu_read_lock (); -list_for_each_entry_rcu (entry, &pernet->local_addr_list, list) { -if (mptcp_addresses_equal (&entry->addr, skc, entry->addr.port)) { - ret = entry->addr.id; -break ; - } - } + entry =__lookup_addr (pernet, skc); + ret = entry ? entry->addr.id :-1 ;rcu_read_unlock ();if (ret >=0 )return ret;
@@ -1315,15 +1311,11 @@ bool mptcp_pm_nl_is_backup(struct mptcp_sock *msk, struct mptcp_addr_info *skc) {struct pm_nl_pernet *pernet =pm_nl_get_pernet_from_msk (msk);struct mptcp_pm_addr_entry *entry; -bool backup = false; +bool backup;rcu_read_lock (); -list_for_each_entry_rcu (entry, &pernet->local_addr_list, list) { -if (mptcp_addresses_equal (&entry->addr, skc, entry->addr.port)) { - backup = !!(entry->flags &MPTCP_PM_ADDR_FLAG_BACKUP ); -break ; - } - } + entry =__lookup_addr (pernet, skc); + backup = entry && !!(entry->flags &MPTCP_PM_ADDR_FLAG_BACKUP );rcu_read_unlock ();return backup;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/4 mptcp: add mptcp_userspace_pm_lookup_addr helper
From: Geliang Tang <[email protected]>
[ Upstream commit e7b4083b90b7213902124d13fd1ed808360e32b1 ]
Like __lookup_addr() helper in pm_netlink.c, a new helper mptcp_userspace_pm_lookup_addr() is also defined in pm_userspace.c. It looks up the corresponding mptcp_pm_addr_entry address in userspace_pm_local_addr_list through the passed "addr" parameter and returns the found address entry.
This helper can be used in mptcp_userspace_pm_delete_local_addr(), mptcp_userspace_pm_set_flags(), mptcp_userspace_pm_get_local_id() and mptcp_userspace_pm_is_backup() to simplify the code.
Please note that with this change now list_for_each_entry() is used in mptcp_userspace_pm_append_new_local_addr(), not list_for_each_entry_safe(), but that's OK to do so because mptcp_userspace_pm_lookup_addr() only returns an entry from the list, the list hasn't been modified here.
Stable-dep-of: 9bc6d5e4ca9f ("mptcp: pm: userspace: fix use-after-free in get_local_id")
--- net/mptcp/pm_userspace.c | 71 ++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 35 deletions(-)
net/mptcp/pm_userspace.c +36 -35
@@ -26,6 +26,19 @@ void mptcp_free_local_addr_list(struct mptcp_sock *msk) } } +static struct mptcp_pm_addr_entry * +mptcp_userspace_pm_lookup_addr (struct mptcp_sock *msk, +const struct mptcp_addr_info *addr) +{ +struct mptcp_pm_addr_entry *entry; + +list_for_each_entry (entry, &msk->pm.userspace_pm_local_addr_list, list) { +if (mptcp_addresses_equal (&entry->addr, addr, false)) +return entry; + } +return NULL ; +} +static int mptcp_userspace_pm_append_new_local_addr (struct mptcp_sock *msk,struct mptcp_pm_addr_entry *entry,bool needs_id)
@@ -90,22 +103,20 @@ static int mptcp_userspace_pm_append_new_local_addr(struct mptcp_sock *msk,static int mptcp_userspace_pm_delete_local_addr (struct mptcp_sock *msk,struct mptcp_pm_addr_entry *addr) { -struct mptcp_pm_addr_entry *entry, *tmp;struct sock *sk = (struct sock *)msk; +struct mptcp_pm_addr_entry *entry; -list_for_each_entry_safe (entry, tmp, &msk->pm.userspace_pm_local_addr_list, list) { -if (mptcp_addresses_equal (&entry->addr, &addr->addr, false)) { -/* TODO: a refcount is needed because the entry can -* be used multiple times (e.g. fullmesh mode). -*/ -list_del_rcu (&entry->list); -sock_kfree_s (sk, entry,sizeof (*entry)); - msk->pm.local_addr_used--; -return 0 ; - } - } - -return -EINVAL ; + entry =mptcp_userspace_pm_lookup_addr (msk, &addr->addr); +if (!entry) +return -EINVAL ; + +/* TODO: a refcount is needed because the entry can +* be used multiple times (e.g. fullmesh mode). +*/ +list_del_rcu (&entry->list); +sock_kfree_s (sk, entry,sizeof (*entry)); + msk->pm.local_addr_used--; +return 0 ; } static struct mptcp_pm_addr_entry *
@@ -123,17 +134,12 @@ mptcp_userspace_pm_lookup_addr_by_id(struct mptcp_sock *msk, unsigned int id)int mptcp_userspace_pm_get_local_id (struct mptcp_sock *msk,struct mptcp_addr_info *skc) { -struct mptcp_pm_addr_entry *entry =NULL , *e, new_entry; +struct mptcp_pm_addr_entry *entry =NULL , new_entry;__be16 msk_sport = ((struct inet_sock *)inet_sk ((struct sock *)msk))->inet_sport;spin_lock_bh (&msk->pm.lock); -list_for_each_entry (e, &msk->pm.userspace_pm_local_addr_list, list) { -if (mptcp_addresses_equal (&e->addr, skc, false)) { - entry = e; -break ; - } - } + entry =mptcp_userspace_pm_lookup_addr (msk, skc);spin_unlock_bh (&msk->pm.lock);if (entry)return entry->addr.id;
@@ -153,15 +159,11 @@ bool mptcp_userspace_pm_is_backup(struct mptcp_sock *msk, struct mptcp_addr_info *skc) {struct mptcp_pm_addr_entry *entry; -bool backup = false; +bool backup;spin_lock_bh (&msk->pm.lock); -list_for_each_entry (entry, &msk->pm.userspace_pm_local_addr_list, list) { -if (mptcp_addresses_equal (&entry->addr, skc, false)) { - backup = !!(entry->flags &MPTCP_PM_ADDR_FLAG_BACKUP ); -break ; - } - } + entry =mptcp_userspace_pm_lookup_addr (msk, skc); + backup = entry && !!(entry->flags &MPTCP_PM_ADDR_FLAG_BACKUP );spin_unlock_bh (&msk->pm.lock);return backup;
@@ -607,13 +609,12 @@ int mptcp_userspace_pm_set_flags(struct sk_buff *skb, struct genl_info *info) bkup =1 ;spin_lock_bh (&msk->pm.lock); -list_for_each_entry (entry, &msk->pm.userspace_pm_local_addr_list, list) { -if (mptcp_addresses_equal (&entry->addr, &loc.addr, false)) { -if (bkup) - entry->flags |=MPTCP_PM_ADDR_FLAG_BACKUP ; -else - entry->flags &= ~MPTCP_PM_ADDR_FLAG_BACKUP ; - } + entry =mptcp_userspace_pm_lookup_addr (msk, &loc.addr); +if (entry) { +if (bkup) + entry->flags |=MPTCP_PM_ADDR_FLAG_BACKUP ; +else + entry->flags &= ~MPTCP_PM_ADDR_FLAG_BACKUP ; }spin_unlock_bh (&msk->pm.lock);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/4 mptcp: pm: use addr entry for get_local_id
In reply to Sasha LevinFrom: Geliang Tang <[email protected]>
[ Upstream commit 7462fe22cc74321eb663768848976d42eba3ddbb ]
The following code in mptcp_userspace_pm_get_local_id() that assigns "skc" to "new_entry" is not allowed in BPF if we use the same code to implement the get_local_id() interface of a BFP path manager:
memset(&new_entry, 0, sizeof(struct mptcp_pm_addr_entry)); new_entry.addr = *skc; new_entry.addr.id = 0; new_entry.flags = MPTCP_PM_ADDR_FLAG_IMPLICIT;
To solve the issue, this patch moves this assignment to "new_entry" forward to mptcp_pm_get_local_id(), and then passing "new_entry" as a parameter to both mptcp_pm_nl_get_local_id() and mptcp_userspace_pm_get_local_id().
No behavioural changes intended.
Stable-dep-of: 9bc6d5e4ca9f ("mptcp: pm: userspace: fix use-after-free in get_local_id")
--- net/mptcp/pm.c | 9 ++++++--- net/mptcp/pm_netlink.c | 11 ++++------- net/mptcp/pm_userspace.c | 17 ++++++----------- net/mptcp/protocol.h | 6 ++++-- 4 files changed, 20 insertions(+), 23 deletions(-)
net/mptcp/pm.c +6 -3
@@ -427,7 +427,7 @@ bool mptcp_pm_rm_addr_signal(struct mptcp_sock *msk, unsigned int remaining,int mptcp_pm_get_local_id (struct mptcp_sock *msk,struct sock_common *skc) { -struct mptcp_addr_info skc_local; +struct mptcp_pm_addr_entry skc_local = {0 };struct mptcp_addr_info msk_local;if (WARN_ON_ONCE (!msk))
@@ -437,10 +437,13 @@ int mptcp_pm_get_local_id(struct mptcp_sock *msk, struct sock_common *skc)* addr */ mptcp_local_address ((struct sock_common *)msk, &msk_local); -mptcp_local_address ((struct sock_common *)skc, &skc_local); -if (mptcp_addresses_equal (&msk_local, &skc_local, false)) +mptcp_local_address ((struct sock_common *)skc, &skc_local.addr); +if (mptcp_addresses_equal (&msk_local, &skc_local.addr, false))return 0 ; + skc_local.addr.id =0 ; + skc_local.flags =MPTCP_PM_ADDR_FLAG_IMPLICIT ; +if (mptcp_pm_is_userspace (msk))return mptcp_userspace_pm_get_local_id (msk, &skc_local);return mptcp_pm_nl_get_local_id (msk, &skc_local);
net/mptcp/pm_netlink.c +4 -7
@@ -1274,7 +1274,8 @@ static int mptcp_pm_nl_create_listen_socket(struct sock *sk,return err; } -int mptcp_pm_nl_get_local_id (struct mptcp_sock *msk,struct mptcp_addr_info *skc) +int mptcp_pm_nl_get_local_id (struct mptcp_sock *msk, +struct mptcp_pm_addr_entry *skc) {struct mptcp_pm_addr_entry *entry;struct pm_nl_pernet *pernet;
@@ -1283,7 +1284,7 @@ int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct mptcp_addr_info *skc pernet =pm_nl_get_pernet_from_msk (msk);rcu_read_lock (); - entry =__lookup_addr (pernet, skc); + entry =__lookup_addr (pernet, &skc->addr); ret = entry ? entry->addr.id :-1 ;rcu_read_unlock ();if (ret >=0 )
@@ -1294,12 +1295,8 @@ int mptcp_pm_nl_get_local_id(struct mptcp_sock *msk, struct mptcp_addr_info *skcif (!entry)return -ENOMEM ; - entry->addr = *skc; - entry->addr.id =0 ; + *entry = *skc; entry->addr.port =0 ; - entry->ifindex =0 ; - entry->flags =MPTCP_PM_ADDR_FLAG_IMPLICIT ; - entry->lsk =NULL ; ret =mptcp_pm_nl_append_new_local_addr (pernet, entry, false);if (ret <0 )kfree (entry);
net/mptcp/pm_userspace.c +6 -11
@@ -132,27 +132,22 @@ mptcp_userspace_pm_lookup_addr_by_id(struct mptcp_sock *msk, unsigned int id) }int mptcp_userspace_pm_get_local_id (struct mptcp_sock *msk, -struct mptcp_addr_info *skc) +struct mptcp_pm_addr_entry *skc) { -struct mptcp_pm_addr_entry *entry =NULL , new_entry;__be16 msk_sport = ((struct inet_sock *)inet_sk ((struct sock *)msk))->inet_sport; +struct mptcp_pm_addr_entry *entry;spin_lock_bh (&msk->pm.lock); - entry =mptcp_userspace_pm_lookup_addr (msk, skc); + entry =mptcp_userspace_pm_lookup_addr (msk, &skc->addr);spin_unlock_bh (&msk->pm.lock);if (entry)return entry->addr.id; -memset (&new_entry,0 ,sizeof (struct mptcp_pm_addr_entry )); - new_entry.addr = *skc; - new_entry.addr.id =0 ; - new_entry.flags =MPTCP_PM_ADDR_FLAG_IMPLICIT ; - -if (new_entry.addr.port == msk_sport) - new_entry.addr.port =0 ; +if (skc->addr.port == msk_sport) + skc->addr.port =0 ; -return mptcp_userspace_pm_append_new_local_addr (msk, &new_entry, true); +return mptcp_userspace_pm_append_new_local_addr (msk, skc, true); } bool mptcp_userspace_pm_is_backup(struct mptcp_sock *msk,
net/mptcp/protocol.h +4 -2
@@ -1136,8 +1136,10 @@ bool mptcp_pm_add_addr_signal(struct mptcp_sock *msk, unsigned int opt_size,bool mptcp_pm_rm_addr_signal (struct mptcp_sock *msk,unsigned int remaining,struct mptcp_rm_list *rm_list);int mptcp_pm_get_local_id (struct mptcp_sock *msk,struct sock_common *skc); -int mptcp_pm_nl_get_local_id (struct mptcp_sock *msk,struct mptcp_addr_info *skc); -int mptcp_userspace_pm_get_local_id (struct mptcp_sock *msk,struct mptcp_addr_info *skc); +int mptcp_pm_nl_get_local_id (struct mptcp_sock *msk, +struct mptcp_pm_addr_entry *skc); +int mptcp_userspace_pm_get_local_id (struct mptcp_sock *msk, +struct mptcp_pm_addr_entry *skc);bool mptcp_pm_is_backup (struct mptcp_sock *msk,struct sock_common *skc);bool mptcp_pm_nl_is_backup (struct mptcp_sock *msk,struct mptcp_addr_info *skc);bool mptcp_userspace_pm_is_backup (struct mptcp_sock *msk,struct mptcp_addr_info *skc);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/4 mptcp: pm: userspace: fix use-after-free in get_local_id
In reply to Sasha LevinFrom: Geliang Tang <[email protected]>
[ Upstream commit 9bc6d5e4ca9f3cbb41d43400b3a31cb0403796c9 ]
In mptcp_pm_userspace_get_local_id(), the address entry is looked up under spinlock, but its id is read after dropping the lock. A concurrent deletion can free the entry between the unlock and the read, leading to UAF.
The race window is narrow. It was reproduced only with a locally constructed stress test that repeatedly overlaps an MP_JOIN SYN with a MPTCP_PM_CMD_SUBFLOW_DESTROY request.
However, the KASAN report below confirms that the race is reachable:
[ 666.319376] BUG: KASAN: slab-use-after-free in mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319386] Read of size 1 at addr ffff888124845610 by task swapper/0/0 ... [ 666.319401] Call Trace: [ 666.319405] <IRQ> [ 666.319408] dump_stack_lvl+0x53/0x70 [ 666.319412] print_address_description.constprop.0+0x2c/0x3b0 [ 666.319418] print_report+0xbe/0x2b0 [ 666.319421] ? mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319423] kasan_report+0xce/0x100 [ 666.319426] ? mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319429] mptcp_userspace_pm_get_local_id+0x1dc/0x1f0 [ 666.319433] mptcp_pm_get_local_id+0x371/0x440 ... [ 666.319821] Allocated by task 45539: [ 666.319844] kasan_save_stack+0x33/0x60 [ 666.319855] kasan_save_track+0x14/0x30 [ 666.319858] __kasan_kmalloc+0x8f/0xa0 [ 666.319863] __kmalloc_noprof+0x1e7/0x520 [ 666.319867] sock_kmalloc+0xdf/0x130 [ 666.319885] sock_kmemdup+0x1b/0x40 [ 666.319888] mptcp_userspace_pm_append_new_local_addr+0x261/0x500 [ 666.319910] mptcp_pm_nl_announce_doit+0x16a/0x610 ... [ 666.319967] Freed by task 45560: [ 666.319988] kasan_save_stack+0x33/0x60 [ 666.319991] kasan_save_track+0x14/0x30 [ 666.319994] kasan_save_free_info+0x3b/0x60 [ 666.319998] __kasan_slab_free+0x43/0x70 [ 666.320000] kfree+0x166/0x440 [ 666.320003] sock_kfree_s+0x1d/0x50 [ 666.320007] mptcp_userspace_pm_delete_local_addr.isra.0+0x157/0x200 [ 666.320011] mptcp_pm_nl_subflow_destroy_doit+0x51d/0xea0
Fix by copying the id into a local variable while still holding the lock, and use -1 as a "not found" sentinel.
--- net/mptcp/pm_userspace.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
net/mptcp/pm_userspace.c +5 -2
@@ -137,12 +137,15 @@ int mptcp_userspace_pm_get_local_id(struct mptcp_sock *msk,__be16 msk_sport = ((struct inet_sock *)inet_sk ((struct sock *)msk))->inet_sport;struct mptcp_pm_addr_entry *entry; +int id;spin_lock_bh (&msk->pm.lock); entry =mptcp_userspace_pm_lookup_addr (msk, &skc->addr); + id = entry ? entry->addr.id :-1 ;spin_unlock_bh (&msk->pm.lock); -if (entry) -return entry->addr.id; + +if (id !=-1 ) +return id;if (skc->addr.port == msk_sport) skc->addr.port =0 ;
Signature · 1 line
2.53.0