net/mptcp/pm.c | 6 ++++-- net/mptcp/pm_userspace.c | 20 +++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-)
From: Quanye Yang <quanyeyang@proton.me>
The userspace PM increments extra_subflows with no upper bound. The
field is a u8, so the 256th extra subflow wraps the counter to 0 and
the next close hits WARN_ON_ONCE().
Refuse admission at U8_MAX for incoming MP_JOIN and for the Netlink
create path.
Fixes: 77e4b94a3de6 ("mptcp: update userspace pm infos")
Cc: stable@vger.kernel.org
Link: https://github.com/multipath-tcp/mptcp_net-next/issues/629
Signed-off-by: Quanye Yang <quanyeyang@proton.me>
---
net/mptcp/pm.c | 6 ++++--
net/mptcp/pm_userspace.c | 20 +++++++++++++-------
2 files changed, 17 insertions(+), 9 deletions(-)
diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index b0b71adefb8f..d2ea0bd39a69 100644
--- a/net/mptcp/pm.c
+++ b/net/mptcp/pm.c
@@ -563,9 +563,11 @@ bool mptcp_pm_allow_new_subflow(struct mptcp_sock *msk)
if (mptcp_pm_is_userspace(msk)) {
if (mptcp_userspace_pm_active(msk)) {
spin_lock_bh(&pm->lock);
- pm->extra_subflows++;
+ ret = pm->extra_subflows < U8_MAX;
+ if (ret)
+ pm->extra_subflows++;
spin_unlock_bh(&pm->lock);
- return true;
+ return ret;
}
return false;
}
diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c
index fab16d953dbf..6d798dd96702 100644
--- a/net/mptcp/pm_userspace.c
+++ b/net/mptcp/pm_userspace.c
@@ -427,16 +427,22 @@ int mptcp_pm_nl_subflow_create_doit(struct sk_buff *skb, struct genl_info *info)
local.ifindex = entry.ifindex;
spin_lock_bh(&msk->pm.lock);
- msk->pm.extra_subflows++;
- spin_unlock_bh(&msk->pm.lock);
+ if (msk->pm.extra_subflows == U8_MAX) {
+ spin_unlock_bh(&msk->pm.lock);
+ GENL_SET_ERR_MSG(info, "too many extra subflows");
+ err = -ENOSPC;
+ } else {
+ msk->pm.extra_subflows++;
+ spin_unlock_bh(&msk->pm.lock);
- lock_sock(sk);
- err = __mptcp_subflow_connect(sk, &local, &addr_r);
- release_sock(sk);
+ lock_sock(sk);
+ err = __mptcp_subflow_connect(sk, &local, &addr_r);
+ release_sock(sk);
+ if (err)
+ GENL_SET_ERR_MSG_FMT(info, "connect error: %d", err);
+ }
if (err) {
- GENL_SET_ERR_MSG_FMT(info, "connect error: %d", err);
-
spin_lock_bh(&msk->pm.lock);
mptcp_userspace_pm_delete_local_addr(msk, &entry);
spin_unlock_bh(&msk->pm.lock);
---
base-commit: 9fbdf9ec14b806afdf66ea265477cb9d4790c866
change-id: 20260902-mptcp-pm-extra-subflows-dfaf79f1f198
Best regards,
--
Quanye Yang <quanyeyang@proton.me>
Hi Quanye,
Thank you for your modifications, that's great!
Our CI did some validations and here is its report:
- KVM Validation: normal (except selftest_mptcp_join): Success! ✅
- KVM Validation: normal (only selftest_mptcp_join): Success! ✅
- KVM Validation: debug (except selftest_mptcp_join): Success! ✅
- KVM Validation: debug (only selftest_mptcp_join): Success! ✅
- KVM Validation: btf-normal (only bpftest_all): Success! ✅
- KVM Validation: btf-debug (only bpftest_all): Success! ✅
- Perf:
- Task: https://github.com/multipath-tcp/mptcp_net-next/actions/runs/33652679146
Initiator: Patchew Applier
Commits: https://github.com/multipath-tcp/mptcp_net-next/commits/6c23d697943d
Patchwork: https://patchwork.kernel.org/project/mptcp/list/?series=1156285
If there are some issues, you can reproduce them using the same environment as
the one used by the CI thanks to a docker image, e.g.:
$ cd [kernel source code]
$ docker run -v "${PWD}:${PWD}:rw" -w "${PWD}" --privileged --rm -it \
--pull always mptcp/mptcp-upstream-virtme-docker:latest \
auto-normal
For more details:
https://github.com/multipath-tcp/mptcp-upstream-virtme-docker
Please note that despite all the efforts that have been already done to have a
stable tests suite when executed on a public CI like here, it is possible some
reported issues are not due to your modifications. Still, do not hesitate to
help us improve that ;-)
Cheers,
MPTCP GH Action bot
Bot operated by Matthieu Baerts (NGI0 Core)
Hi Quanye,
+ Cc: Tao Cui
On 02/09/2026 17:39, Quanye Yang via B4 Relay wrote:
> From: Quanye Yang <quanyeyang@proton.me>
>
> The userspace PM increments extra_subflows with no upper bound. The
> field is a u8, so the 256th extra subflow wraps the counter to 0 and
> the next close hits WARN_ON_ONCE().
>
> Refuse admission at U8_MAX for incoming MP_JOIN and for the Netlink
> create path.
>
> Fixes: 77e4b94a3de6 ("mptcp: update userspace pm infos")
> Cc: stable@vger.kernel.org
(No need to add stable on patches sent to the MPTCP ML, that will be
added later when sending these patches to netdev)
> Link: https://github.com/multipath-tcp/mptcp_net-next/issues/629
"Closes:" can be used here.
(...)
> diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c
> index fab16d953dbf..6d798dd96702 100644
> --- a/net/mptcp/pm_userspace.c
> +++ b/net/mptcp/pm_userspace.c
> @@ -427,16 +427,22 @@ int mptcp_pm_nl_subflow_create_doit(struct sk_buff *skb, struct genl_info *info)
> local.ifindex = entry.ifindex;
>
> spin_lock_bh(&msk->pm.lock);
> - msk->pm.extra_subflows++;
> - spin_unlock_bh(&msk->pm.lock);
> + if (msk->pm.extra_subflows == U8_MAX) {
> + spin_unlock_bh(&msk->pm.lock);
> + GENL_SET_ERR_MSG(info, "too many extra subflows");
> + err = -ENOSPC;
Maybe a goto could be used here ...
> + } else {
> + msk->pm.extra_subflows++;
> + spin_unlock_bh(&msk->pm.lock);
>
> - lock_sock(sk);
> - err = __mptcp_subflow_connect(sk, &local, &addr_r);
> - release_sock(sk);
> + lock_sock(sk);
> + err = __mptcp_subflow_connect(sk, &local, &addr_r);
> + release_sock(sk);
> + if (err)
> + GENL_SET_ERR_MSG_FMT(info, "connect error: %d", err);
> + }
>
> if (err) {
> - GENL_SET_ERR_MSG_FMT(info, "connect error: %d", err);
> -
... to here, not to modify the rest of the code?
> spin_lock_bh(&msk->pm.lock);
> mptcp_userspace_pm_delete_local_addr(msk, &entry);
> spin_unlock_bh(&msk->pm.lock);
This part hasn't been fixed on Tao's series [1]. Would it be OK for both
of you if this part of Quanye's patch is added to Tao's v2 series with
Quanye as author?
[1] https://lore.kernel.org/20260831093206.689827-1-cui.tao@linux.dev
Cheers,
Matt
--
Sponsored by the NGI0 Core fund.
Hi Quanye, +cc Tao On 02/09/2026 17:39, Quanye Yang via B4 Relay wrote: > From: Quanye Yang <quanyeyang@proton.me> > > The userspace PM increments extra_subflows with no upper bound. The > field is a u8, so the 256th extra subflow wraps the counter to 0 and > the next close hits WARN_ON_ONCE(). > > Refuse admission at U8_MAX for incoming MP_JOIN and for the Netlink > create path. Thank you for the patch. It looks like Tao already addresses the same issues, see: https://lore.kernel.org/20260831093206.689827-1-cui.tao@linux.dev Do you mind syncing with him, please? Cheers, Matt -- Sponsored by the NGI0 Core fund.
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Error paths in mptcp_pm_nl_subflow_create_doit unconditionally delete pre-existing local addresses, corrupting state and underflowing local_addr_used.
--
commit 09af196be93604649e0aad5970acd8810bf5dcca
Author: Quanye Yang <quanyeyang@proton.me>
mptcp: pm: cap userspace extra_subflows at U8_MAX
The userspace PM increments extra_subflows with no upper bound. The
field is a u8, so the 256th extra subflow wraps the counter to 0 and
the next close hits WARN_ON_ONCE().
Link: https://github.com/multipath-tcp/mptcp_net-next/issues/629
> diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c
> index fab16d953dbfe..6d798dd967029 100644
> --- a/net/mptcp/pm_userspace.c
> +++ b/net/mptcp/pm_userspace.c
> @@ -427,16 +427,22 @@ int mptcp_pm_nl_subflow_create_doit(struct sk_buff *skb, struct genl_info *info)
> local.ifindex = entry.ifindex;
>
> spin_lock_bh(&msk->pm.lock);
> - msk->pm.extra_subflows++;
> - spin_unlock_bh(&msk->pm.lock);
> + if (msk->pm.extra_subflows == U8_MAX) {
> + spin_unlock_bh(&msk->pm.lock);
> + GENL_SET_ERR_MSG(info, "too many extra subflows");
> + err = -ENOSPC;
> + } else {
> + msk->pm.extra_subflows++;
> + spin_unlock_bh(&msk->pm.lock);
>
> - lock_sock(sk);
> - err = __mptcp_subflow_connect(sk, &local, &addr_r);
> - release_sock(sk);
> + lock_sock(sk);
> + err = __mptcp_subflow_connect(sk, &local, &addr_r);
> + release_sock(sk);
> + if (err)
> + GENL_SET_ERR_MSG_FMT(info, "connect error: %d", err);
> + }
>
> if (err) {
> - GENL_SET_ERR_MSG_FMT(info, "connect error: %d", err);
> -
> spin_lock_bh(&msk->pm.lock);
> mptcp_userspace_pm_delete_local_addr(msk, &entry);
> spin_unlock_bh(&msk->pm.lock);
[Severity: High]
This is a pre-existing issue, but does this error path incorrectly delete
pre-existing local addresses?
If a user issues MPTCP_PM_CMD_SUBFLOW_CREATE using a local address that was
already appended, mptcp_userspace_pm_append_new_local_addr() returns success
without allocating a new entry or incrementing local_addr_used.
However, if the new subflow connection fails (or if the newly introduced
extra_subflows == U8_MAX limit is hit), the error handling path blindly invokes
mptcp_userspace_pm_delete_local_addr(msk, &entry) inside
mptcp_pm_nl_subflow_create_doit().
Will this inadvertently destroy pre-existing PM state for existing subflows
relying on that address, and underflow the 8-bit msk->pm.local_addr_used
counter since it wasn't incremented?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260902-mptcp-pm-extra-subflows-v1-1-68540a866e5a@proton.me?part=1
© 2016 - 2026 Red Hat, Inc.