MPTCP Linux Development
 help / color / mirror / Atom feed
* [PATCH mptcp-next v3 0/3] mptcp: pm: fix reachable extra_subflows guards
@ 2026-09-04  5:11 Tao Cui
  2026-09-04  5:11 ` [PATCH mptcp-next v3 1/3] mptcp: pm: bound extra_subflows admission on userspace PM Tao Cui
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Tao Cui @ 2026-09-04  5:11 UTC (permalink / raw)
  To: mptcp, matttbe, quanyeyang; +Cc: geliang, cuitao, cui.tao

From: Tao Cui <cuitao@kylinos.cn>

Hi,

Following up on issue #629, here is a fix series for the issues
reported against e99c1ca871 ("mptcp: pm: add WARN_ON_ONCE guards
on extra_subflows underflow").

Patch 1 bounds the userspace PM admission at U8_MAX, so the u8 can
no longer be wrapped by a peer establishing more than 255 subflows.

Patch 2 addresses the disconnect() vs MP_JOIN race: as discussed,
the decrement sites now check the msk state first and skip the
accounting when the msk is already in TCP_CLOSE, as the state is set
before mptcp_pm_data_reset() clears the counters, and once the msk
is closed the accounting is not relevant anymore. No new lock is
involved. The guards themselves are downgraded to a clamp with a
rate-limited pr_warn(), as the warn was reachable and turned into a
remotely triggerable panic on panic_on_warn kernels.

Patch 3, from Quanye, applies the same bound to the Netlink subflow
creation path.

Changes since v2:

- Patch 3: label moved inside the if-statement, a note about the
  pre-existing unconditional local address deletion in the reused
  error path, and the spurious empty line before the SoBs dropped

Changes since v1 [1]:

- Patch 1: re-use "ret" instead of a new variable, drop the
  comment, Co-developed-by added for Quanye, who sent the same fix
  independently
- issue reference: Link: on patches 1 and 2, Closes: on the last one
- New patch 3 from Quanye for the Netlink path, per the discussion
  with Matt [2]

Validated with the virtme CI (mptcp selftests), and with a stress
repro comparing the base kernel and the patched one: on the base
kernel the server accepts 256 MP_JOINs and the counter wraps to 0,
with the patches admission stops at 255 and the leftover imbalance
only shows up as a rate-limited warning.

[1] https://lore.kernel.org/20260831093206.689827-1-cui.tao@linux.dev/
[2] https://lore.kernel.org/all/c8c4f667-5d25-4d94-8627-387d4091c8c4@kernel.org/

Quanye Yang (1):
  mptcp: pm: userspace: cap extra_subflows on Netlink subflow creation

Tao Cui (2):
  mptcp: pm: bound extra_subflows admission on userspace PM
  mptcp: pm: skip extra_subflows accounting on disconnected msk

 net/mptcp/pm.c           | 17 ++++++++++++++---
 net/mptcp/pm_userspace.c |  7 +++++++
 net/mptcp/protocol.h     | 14 ++++++++++++--
 3 files changed, 33 insertions(+), 5 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH mptcp-next v3 1/3] mptcp: pm: bound extra_subflows admission on userspace PM
  2026-09-04  5:11 [PATCH mptcp-next v3 0/3] mptcp: pm: fix reachable extra_subflows guards Tao Cui
@ 2026-09-04  5:11 ` Tao Cui
  2026-09-04  5:11 ` [PATCH mptcp-next v3 2/3] mptcp: pm: skip extra_subflows accounting on disconnected msk Tao Cui
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Tao Cui @ 2026-09-04  5:11 UTC (permalink / raw)
  To: mptcp, matttbe, quanyeyang; +Cc: geliang, cuitao, cui.tao

From: Tao Cui <cuitao@kylinos.cn>

mptcp_pm_allow_new_subflow() increments the u8 extra_subflows counter
for every accepted MP_JOIN on sockets using the userspace PM, without
any limit. A peer establishing more than 255 live subflows wraps the
counter back to 0, which then makes the underflow guards warn on the
next subflow close, and permanently corrupts mptcpi_subflows_total
reported to userspace.

Refuse new MP_JOINs once the counter has reached U8_MAX, so that it
cannot wrap anymore.

Fixes: e99c1ca89071 ("mptcp: pm: add WARN_ON_ONCE guards on extra_subflows underflow")
Link: https://github.com/multipath-tcp/mptcp_net-next/issues/629
Co-developed-by: Quanye Yang <quanyeyang@proton.me>
Signed-off-by: Quanye Yang <quanyeyang@proton.me>
Signed-off-by: Tao Cui <cuitao@kylinos.cn>
---
 net/mptcp/pm.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index 8b68868255c5..07cdcdb54b15 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;
 	}
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH mptcp-next v3 2/3] mptcp: pm: skip extra_subflows accounting on disconnected msk
  2026-09-04  5:11 [PATCH mptcp-next v3 0/3] mptcp: pm: fix reachable extra_subflows guards Tao Cui
  2026-09-04  5:11 ` [PATCH mptcp-next v3 1/3] mptcp: pm: bound extra_subflows admission on userspace PM Tao Cui
@ 2026-09-04  5:11 ` Tao Cui
  2026-09-04  5:28   ` sashiko-bot
  2026-09-04  5:11 ` [PATCH mptcp-next v3 3/3] mptcp: pm: userspace: cap extra_subflows on Netlink subflow creation Tao Cui
  2026-09-04  6:29 ` [PATCH mptcp-next v3 0/3] mptcp: pm: fix reachable extra_subflows guards MPTCP CI
  3 siblings, 1 reply; 6+ messages in thread
From: Tao Cui @ 2026-09-04  5:11 UTC (permalink / raw)
  To: mptcp, matttbe, quanyeyang; +Cc: geliang, cuitao, cui.tao

From: Tao Cui <cuitao@kylinos.cn>

The WARN_ON_ONCE() guards added to the extra_subflows decrement sites
turn out to be reachable:

mptcp_pm_data_reset() zeroes the counter with only the msk socket lock
held, while an MP_JOIN subflow can still sit in msk->join_list, its
reference already accounted by mptcp_pm_allow_new_subflow() under
pm->lock. If the socket gets disconnected(AF_UNSPEC) in that window,
mptcp_pm_data_reset() zeroes the counter, and the join list is flushed
later at release_sock() time: the leftover subflow then reaches
mptcp_pm_subflow_check_next() (or __mptcp_pm_close_subflow() for
kernel PM sockets) with the counter already at 0, firing the warning.
On panic_on_warn kernels this is a remotely triggerable panic, which
is worse than the silent wrap the guards replaced.

Skip the PM accounting when the msk is already in TCP_CLOSE: in the
scenario above the state is set before the counters are cleared, and
once the msk is closed the accounting is not relevant anymore. Keep a
clamp and a rate-limited pr_warn() on the decrement sites instead,
to leave a trace of any imbalance we would still not know about.

Fixes: e99c1ca89071 ("mptcp: pm: add WARN_ON_ONCE guards on extra_subflows underflow")
Link: https://github.com/multipath-tcp/mptcp_net-next/issues/629
Suggested-by: Matthieu Baerts <matttbe@kernel.org>
Signed-off-by: Tao Cui <cuitao@kylinos.cn>
---
 net/mptcp/pm.c       | 11 ++++++++++-
 net/mptcp/protocol.h | 14 ++++++++++++--
 2 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
index 07cdcdb54b15..cccf5319cce0 100644
--- a/net/mptcp/pm.c
+++ b/net/mptcp/pm.c
@@ -671,9 +671,18 @@ void mptcp_pm_subflow_check_next(struct mptcp_sock *msk,
 	update_subflows = subflow->request_join || subflow->mp_join;
 	if (mptcp_pm_is_userspace(msk)) {
 		if (update_subflows) {
+			/* The PM counters have already been cleared if the
+			 * msk got disconnected while this subflow was still
+			 * queued in the join list
+			 */
+			if (inet_sk_state_load(sk) == TCP_CLOSE)
+				return;
 			spin_lock_bh(&pm->lock);
-			if (!WARN_ON_ONCE(pm->extra_subflows == 0))
+			if (likely(pm->extra_subflows))
 				pm->extra_subflows--;
+			else
+				pr_warn_ratelimited("extra_subflows underflow, msk=%p\n",
+						    msk);
 			spin_unlock_bh(&pm->lock);
 		}
 		return;
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 87ccb84e9927..07821551499c 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -1252,8 +1252,18 @@ u8 mptcp_pm_get_limit_extra_subflows(const struct mptcp_sock *msk);
 /* called under PM lock */
 static inline void __mptcp_pm_close_subflow(struct mptcp_sock *msk)
 {
-	if (!WARN_ON_ONCE(msk->pm.extra_subflows == 0) &&
-	    --msk->pm.extra_subflows < mptcp_pm_get_limit_extra_subflows(msk))
+	/* The PM counters have already been cleared if the msk got
+	 * disconnected while this subflow was still queued in the
+	 * join list
+	 */
+	if (inet_sk_state_load((struct sock *)msk) == TCP_CLOSE)
+		return;
+
+	if (unlikely(msk->pm.extra_subflows == 0)) {
+		pr_warn_ratelimited("extra_subflows underflow, msk=%p\n", msk);
+		return;
+	}
+	if (--msk->pm.extra_subflows < mptcp_pm_get_limit_extra_subflows(msk))
 		WRITE_ONCE(msk->pm.accept_subflow, true);
 }
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH mptcp-next v3 3/3] mptcp: pm: userspace: cap extra_subflows on Netlink subflow creation
  2026-09-04  5:11 [PATCH mptcp-next v3 0/3] mptcp: pm: fix reachable extra_subflows guards Tao Cui
  2026-09-04  5:11 ` [PATCH mptcp-next v3 1/3] mptcp: pm: bound extra_subflows admission on userspace PM Tao Cui
  2026-09-04  5:11 ` [PATCH mptcp-next v3 2/3] mptcp: pm: skip extra_subflows accounting on disconnected msk Tao Cui
@ 2026-09-04  5:11 ` Tao Cui
  2026-09-04  6:29 ` [PATCH mptcp-next v3 0/3] mptcp: pm: fix reachable extra_subflows guards MPTCP CI
  3 siblings, 0 replies; 6+ messages in thread
From: Tao Cui @ 2026-09-04  5:11 UTC (permalink / raw)
  To: mptcp, matttbe, quanyeyang; +Cc: geliang, cuitao, cui.tao

From: Quanye Yang <quanyeyang@proton.me>

mptcp_pm_nl_subflow_create_doit() also increments the u8 extra_subflows
counter without any limit: a client with CAP_NET_ADMIN can create more
than 255 extra subflows via Netlink and wrap the counter back to 0,
with the same effects as the MP_JOIN path now bounded separately:
the underflow guards warn on the next subflow close, and
mptcpi_subflows_total reported to userspace is corrupted.

Refuse the subflow creation with -ENOSPC once the counter has reached
U8_MAX, and clean up the local address entry in that case as well.

The same error path is followed after
mptcp_userspace_pm_append_new_local_addr, which should probably not
delete the local address unconditionally, but this is a pre-existing
issue that will be addressed separately.

Fixes: 77e4b94a3de6 ("mptcp: update userspace pm infos")
Link: https://lore.kernel.org/all/20260902-mptcp-pm-extra-subflows-v1-1-68540a866e5a@proton.me/
Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/629
Signed-off-by: Quanye Yang <quanyeyang@proton.me>
Signed-off-by: Tao Cui <cuitao@kylinos.cn>
---
 net/mptcp/pm_userspace.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c
index b94fbb483bf9..e7f7d023c246 100644
--- a/net/mptcp/pm_userspace.c
+++ b/net/mptcp/pm_userspace.c
@@ -417,6 +417,12 @@ 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);
+	if (msk->pm.extra_subflows == U8_MAX) {
+		spin_unlock_bh(&msk->pm.lock);
+		GENL_SET_ERR_MSG(info, "too many extra subflows");
+		err = -ENOSPC;
+		goto delete_addr;
+	}
 	msk->pm.extra_subflows++;
 	spin_unlock_bh(&msk->pm.lock);
 
@@ -427,6 +433,7 @@ int mptcp_pm_nl_subflow_create_doit(struct sk_buff *skb, struct genl_info *info)
 	if (err) {
 		GENL_SET_ERR_MSG_FMT(info, "connect error: %d", err);
 
+delete_addr:
 		spin_lock_bh(&msk->pm.lock);
 		mptcp_userspace_pm_delete_local_addr(msk, &entry);
 		spin_unlock_bh(&msk->pm.lock);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH mptcp-next v3 2/3] mptcp: pm: skip extra_subflows accounting on disconnected msk
  2026-09-04  5:11 ` [PATCH mptcp-next v3 2/3] mptcp: pm: skip extra_subflows accounting on disconnected msk Tao Cui
@ 2026-09-04  5:28   ` sashiko-bot
  0 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-09-04  5:28 UTC (permalink / raw)
  To: Tao Cui; +Cc: mptcp

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Data race in mptcp_pm_data_reset() when clearing the pm->reset struct group locklessly, causing torn reads/writes with concurrent PM operations.
--

commit 0c791790f3c719050b1d2004c347405ffb76512f
Author: Tao Cui <cuitao@kylinos.cn>

mptcp: pm: skip extra_subflows accounting on disconnected msk

This commit addresses a remotely triggerable panic caused by a WARN_ON_ONCE
guard on extra_subflows underflow. It skips the PM accounting when the socket
is in TCP_CLOSE, as the counters may have already been cleared by
mptcp_pm_data_reset().

Link: https://github.com/multipath-tcp/mptcp_net-next/issues/629

> diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c
> index 9881092c16e78..520f0c638e6f7 100644
> --- a/net/mptcp/pm.c
> +++ b/net/mptcp/pm.c
> @@ -671,9 +671,18 @@ void mptcp_pm_subflow_check_next(struct mptcp_sock *msk,
>  	update_subflows = subflow->request_join || subflow->mp_join;
>  	if (mptcp_pm_is_userspace(msk)) {
>  		if (update_subflows) {
> +			/* The PM counters have already been cleared if the
> +			 * msk got disconnected while this subflow was still
> +			 * queued in the join list
> +			 */

[Severity: High]
This isn't a bug introduced by this patch, but is there a data race when
clearing the pm->reset struct group locklessly in mptcp_pm_data_reset()?

Looking at net/mptcp/protocol.c:mptcp_disconnect(), it calls
mptcp_pm_data_reset() on an active, network-exposed socket without acquiring
pm->lock:

net/mptcp/protocol.c:mptcp_disconnect() {
    ...
    mptcp_pm_data_reset(msk);
    ...
}

Inside net/mptcp/pm.c:mptcp_pm_data_reset(), the code does a lockless memset
of the struct group:

net/mptcp/pm.c:mptcp_pm_data_reset() {
    ...
    memset(&pm->reset, 0, sizeof(pm->reset));
    ...
}

Could this cause torn reads and writes with concurrent PM operations?

For example, mptcp_pm_allow_new_subflow() concurrently modifies the memory
while holding pm->lock, but without holding the socket lock:

net/mptcp/pm.c:mptcp_pm_allow_new_subflow() {
    ...
    pm->extra_subflows++;
    ...
}

If a client socket initiates a local disconnect via connect(AF_UNSPEC), and
concurrently an in-flight SYN-ACK or remote MP_JOIN request is processed in
softirq context via mptcp_finish_join(), could they interleave?

It seems this might lead to data corruption of the pm->reset struct group
fields (including extra_subflows, accept_subflow, and addr_signal), which
could cause PM misbehavior such as permanent rejection of new subflows on a
reconnected socket.

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260904051150.1196427-1-cui.tao@linux.dev?part=2

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH mptcp-next v3 0/3] mptcp: pm: fix reachable extra_subflows guards
  2026-09-04  5:11 [PATCH mptcp-next v3 0/3] mptcp: pm: fix reachable extra_subflows guards Tao Cui
                   ` (2 preceding siblings ...)
  2026-09-04  5:11 ` [PATCH mptcp-next v3 3/3] mptcp: pm: userspace: cap extra_subflows on Netlink subflow creation Tao Cui
@ 2026-09-04  6:29 ` MPTCP CI
  3 siblings, 0 replies; 6+ messages in thread
From: MPTCP CI @ 2026-09-04  6:29 UTC (permalink / raw)
  To: Quanye Yang; +Cc: mptcp

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/33840713883

Initiator: Patchew Applier
Commits: https://github.com/multipath-tcp/mptcp_net-next/commits/2d0e39ea2ca1
Patchwork: https://patchwork.kernel.org/project/mptcp/list/?series=1157585


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)

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-09-04  6:29 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-04  5:11 [PATCH mptcp-next v3 0/3] mptcp: pm: fix reachable extra_subflows guards Tao Cui
2026-09-04  5:11 ` [PATCH mptcp-next v3 1/3] mptcp: pm: bound extra_subflows admission on userspace PM Tao Cui
2026-09-04  5:11 ` [PATCH mptcp-next v3 2/3] mptcp: pm: skip extra_subflows accounting on disconnected msk Tao Cui
2026-09-04  5:28   ` sashiko-bot
2026-09-04  5:11 ` [PATCH mptcp-next v3 3/3] mptcp: pm: userspace: cap extra_subflows on Netlink subflow creation Tao Cui
2026-09-04  6:29 ` [PATCH mptcp-next v3 0/3] mptcp: pm: fix reachable extra_subflows guards MPTCP CI

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox