* [PATCH mptcp-next v16 1/4] mptcp: drop the mptcp_ooo_try_coalesce() helper
2026-10-29 6:47 [PATCH mptcp-next v16 0/4] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
@ 2026-10-29 6:47 ` Geliang Tang
2026-10-29 6:47 ` [PATCH mptcp-next v16 2/4] mptcp: remove CB offset field Geliang Tang
` (3 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Geliang Tang @ 2026-10-29 6:47 UTC (permalink / raw)
To: mptcp; +Cc: Paolo Abeni, Geliang Tang
From: Paolo Abeni <pabeni@redhat.com>
It's used to save an additional comparison for in-order skbs, but is
also a barrier to remove CB offset. Remove the helper, let
__mptcp_try_coalesce() always perform the sequence check and remove
duplicate checks from the callers.
Co-developed-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
net/mptcp/protocol.c | 21 ++++++---------------
1 file changed, 6 insertions(+), 15 deletions(-)
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 02a4f479d9d8..528a72da646c 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -167,7 +167,8 @@ static bool __mptcp_try_coalesce(struct sock *sk, struct sk_buff *to,
{
int limit = READ_ONCE(sk->sk_rcvbuf);
- if (unlikely(MPTCP_SKB_CB(to)->cant_coalesce) ||
+ if (MPTCP_SKB_CB(from)->map_seq != MPTCP_SKB_CB(to)->end_seq ||
+ unlikely(MPTCP_SKB_CB(to)->cant_coalesce) ||
MPTCP_SKB_CB(from)->offset ||
((to->len + from->len) > (limit >> 3)) ||
!skb_try_coalesce(to, from, fragstolen, delta))
@@ -200,15 +201,6 @@ static bool mptcp_try_coalesce(struct sock *sk, struct sk_buff *to,
return true;
}
-static bool mptcp_ooo_try_coalesce(struct mptcp_sock *msk, struct sk_buff *to,
- struct sk_buff *from)
-{
- if (MPTCP_SKB_CB(from)->map_seq != MPTCP_SKB_CB(to)->end_seq)
- return false;
-
- return mptcp_try_coalesce((struct sock *)msk, to, from);
-}
-
/* "inspired" by tcp_rcvbuf_grow(), main difference:
* - mptcp does not maintain a msk-level window clamp
* - returns true when the receive buffer is actually updated
@@ -348,7 +340,7 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
/* with 2 subflows, adding at end of ooo queue is quite likely
* Use of ooo_last_skb avoids the O(Log(N)) rbtree lookup.
*/
- if (mptcp_ooo_try_coalesce(msk, msk->ooo_last_skb, skb)) {
+ if (mptcp_try_coalesce(sk, msk->ooo_last_skb, skb)) {
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_OFOMERGE);
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_OFOQUEUETAIL);
return;
@@ -394,7 +386,7 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA);
goto merge_right;
}
- } else if (mptcp_ooo_try_coalesce(msk, skb1, skb)) {
+ } else if (mptcp_try_coalesce(sk, skb1, skb)) {
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_OFOMERGE);
return;
}
@@ -777,8 +769,7 @@ static void __mptcp_add_backlog(struct sock *sk,
if (!list_empty(&msk->backlog_list))
tail = list_last_entry(&msk->backlog_list, struct sk_buff, list);
- if (tail && MPTCP_SKB_CB(skb)->map_seq == MPTCP_SKB_CB(tail)->end_seq &&
- ssk == tail->sk &&
+ if (tail && ssk == tail->sk &&
__mptcp_try_coalesce(sk, tail, skb, &fragstolen, &delta)) {
skb->truesize -= delta;
kfree_skb_partial(skb, fragstolen);
@@ -902,7 +893,7 @@ static bool __mptcp_ofo_queue(struct mptcp_sock *msk)
end_seq = MPTCP_SKB_CB(skb)->end_seq;
tail = skb_peek_tail(&sk->sk_receive_queue);
- if (!tail || !mptcp_ooo_try_coalesce(msk, tail, skb)) {
+ if (!tail || !mptcp_try_coalesce(sk, tail, skb)) {
int delta = msk->ack_seq - MPTCP_SKB_CB(skb)->map_seq;
/* skip overlapping data, if any */
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH mptcp-next v16 2/4] mptcp: remove CB offset field
2026-10-29 6:47 [PATCH mptcp-next v16 0/4] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
2026-10-29 6:47 ` [PATCH mptcp-next v16 1/4] mptcp: drop the mptcp_ooo_try_coalesce() helper Geliang Tang
@ 2026-10-29 6:47 ` Geliang Tang
2026-10-29 7:10 ` sashiko-bot
2026-10-29 6:47 ` [PATCH mptcp-next v16 3/4] mptcp: sync mptcp skb cb layout with tcp one Geliang Tang
` (2 subsequent siblings)
4 siblings, 1 reply; 8+ messages in thread
From: Geliang Tang @ 2026-10-29 6:47 UTC (permalink / raw)
To: mptcp; +Cc: Paolo Abeni, Geliang Tang
From: Paolo Abeni <pabeni@redhat.com>
Instead, use a new msk-level field to track the bytes already consumed
inside each skb, carrying the amount of bytes already copied to
user-space, alike what TCP is already doing.
The newly introduce `copied_seq` field is always accessed under the msk
socket lock, delegating the synchronization with IASN to the msk release
CB, when the socket is owned by the user-space at remote key reception
time. Such synchronization preserves any partial progress (copy) made on
the TFO packet.
Note that the explicit synchronization in __mptcp_move_skb() is needed to
ensure that the TFO skb in the receive queue got its map_seq synched
before the next skb lands into the receive queue when spooling the backlog
at mptcp_release_cb() time, as the release CB synchronization will happen
later.
Prior to this patch, the TFO skb dummy mapping (map_seq = -(u32)skb->len,
zero-extended to u64) caused mptcp_inq_hint() to compute a wrapped
ack_seq - map_seq, so SIOCINQ and TCP_CM_INQ returned INT_MAX before the
MPC ACK. This patch fixes that behaviour through both the -(u64)skb->len
sign fix and the new ack_seq - copied_seq hint.
Overall this simplifies a bit the __mptcp_recvmsg_mskq() and the
__mptcp_move_skb() code and will also make possible the next patch.
Co-developed-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
net/mptcp/fastopen.c | 12 ++++++++----
net/mptcp/protocol.c | 42 ++++++++++++++++++------------------------
net/mptcp/protocol.h | 2 +-
net/mptcp/subflow.c | 15 ++++++++++++++-
4 files changed, 41 insertions(+), 30 deletions(-)
diff --git a/net/mptcp/fastopen.c b/net/mptcp/fastopen.c
index f717750906ff..04b1038b5200 100644
--- a/net/mptcp/fastopen.c
+++ b/net/mptcp/fastopen.c
@@ -9,6 +9,7 @@
void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subflow,
struct request_sock *req)
{
+ struct mptcp_sock *msk;
struct sock *sk, *ssk;
struct sk_buff *skb;
struct tcp_sock *tp;
@@ -44,20 +45,23 @@ void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subf
subflow->ssn_offset += skb->len;
has_rxtstamp = TCP_SKB_CB(skb)->has_rxtstamp;
- /* Only the sequence delta is relevant */
- MPTCP_SKB_CB(skb)->map_seq = -skb->len;
+ /* The TFO segment data sits before the IASN; before receiving
+ * the remote key, IASN is assumed being 0.
+ */
+ MPTCP_SKB_CB(skb)->map_seq = -(u64)skb->len;
MPTCP_SKB_CB(skb)->end_seq = 0;
- MPTCP_SKB_CB(skb)->offset = 0;
MPTCP_SKB_CB(skb)->has_rxtstamp = has_rxtstamp;
MPTCP_SKB_CB(skb)->cant_coalesce = 1;
mptcp_data_lock(sk);
DEBUG_NET_WARN_ON_ONCE(sock_owned_by_user_nocheck(sk));
+ msk = mptcp_sk(sk);
+ msk->copied_seq = MPTCP_SKB_CB(skb)->map_seq;
mptcp_borrow_fwdmem(sk, skb);
skb_set_owner_r(skb, sk);
__skb_queue_tail(&sk->sk_receive_queue, skb);
- mptcp_sk(sk)->bytes_received += skb->len;
+ msk->bytes_received += skb->len;
sk->sk_data_ready(sk);
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index 528a72da646c..f89ed94b668d 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -169,7 +169,6 @@ static bool __mptcp_try_coalesce(struct sock *sk, struct sk_buff *to,
if (MPTCP_SKB_CB(from)->map_seq != MPTCP_SKB_CB(to)->end_seq ||
unlikely(MPTCP_SKB_CB(to)->cant_coalesce) ||
- MPTCP_SKB_CB(from)->offset ||
((to->len + from->len) > (limit >> 3)) ||
!skb_try_coalesce(to, from, fragstolen, delta))
return false;
@@ -416,8 +415,7 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
skb_set_owner_r(skb, sk);
}
-static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset,
- int copy_len)
+static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset)
{
struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk);
bool has_rxtstamp = TCP_SKB_CB(skb)->has_rxtstamp;
@@ -426,9 +424,9 @@ static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset,
* mptcp_subflow_get_mapped_dsn() is based on the current tp->copied_seq
* value
*/
- MPTCP_SKB_CB(skb)->map_seq = mptcp_subflow_get_mapped_dsn(subflow);
- MPTCP_SKB_CB(skb)->end_seq = MPTCP_SKB_CB(skb)->map_seq + copy_len;
- MPTCP_SKB_CB(skb)->offset = offset;
+ MPTCP_SKB_CB(skb)->map_seq = mptcp_subflow_get_mapped_dsn(subflow) -
+ offset;
+ MPTCP_SKB_CB(skb)->end_seq = MPTCP_SKB_CB(skb)->map_seq + skb->len;
MPTCP_SKB_CB(skb)->has_rxtstamp = has_rxtstamp;
MPTCP_SKB_CB(skb)->cant_coalesce = 0;
@@ -472,10 +470,6 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
/* Partial packet */
if (after64(MPTCP_SKB_CB(skb)->end_seq, msk->ack_seq)) {
copy_len = MPTCP_SKB_CB(skb)->end_seq - msk->ack_seq;
- MPTCP_SKB_CB(skb)->offset += msk->ack_seq -
- MPTCP_SKB_CB(skb)->map_seq;
- MPTCP_SKB_CB(skb)->map_seq += msk->ack_seq -
- MPTCP_SKB_CB(skb)->map_seq;
goto insert;
}
@@ -833,7 +827,7 @@ static bool __mptcp_move_skbs_from_subflow(struct mptcp_sock *msk,
if (offset < skb->len) {
size_t len = skb->len - offset;
- mptcp_init_skb(ssk, skb, offset, len);
+ mptcp_init_skb(ssk, skb, offset);
if (own_msk) {
mptcp_subflow_lend_fwdmem(subflow, skb);
@@ -896,12 +890,9 @@ static bool __mptcp_ofo_queue(struct mptcp_sock *msk)
if (!tail || !mptcp_try_coalesce(sk, tail, skb)) {
int delta = msk->ack_seq - MPTCP_SKB_CB(skb)->map_seq;
- /* skip overlapping data, if any */
pr_debug("uncoalesced seq=%llx ack seq=%llx delta=%d\n",
MPTCP_SKB_CB(skb)->map_seq, msk->ack_seq,
delta);
- MPTCP_SKB_CB(skb)->offset += delta;
- MPTCP_SKB_CB(skb)->map_seq += delta;
__skb_queue_tail(&sk->sk_receive_queue, skb);
}
msk->bytes_received += end_seq - msk->ack_seq;
@@ -2195,11 +2186,17 @@ static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg,
int copied = 0;
skb_queue_walk_safe(&sk->sk_receive_queue, skb, tmp) {
- u32 delta, offset = MPTCP_SKB_CB(skb)->offset;
+ u32 delta, offset = (u32)msk->copied_seq - MPTCP_SKB_CB(skb)->map_seq;
u32 data_len = skb->len - offset;
u32 count;
int err;
+ /* Skip skbs that are entirely before copied_seq */
+ if (offset >= skb->len) {
+ *last = skb;
+ continue;
+ }
+
if (flags & MSG_PEEK) {
/* skip already peeked skbs */
if (total_data_len + data_len <= copied_total) {
@@ -2233,11 +2230,9 @@ static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg,
if (!(flags & MSG_PEEK)) {
msk->bytes_consumed += count;
- if (count < data_len) {
- MPTCP_SKB_CB(skb)->offset += count;
- MPTCP_SKB_CB(skb)->map_seq += count;
+ msk->copied_seq += count;
+ if (count < data_len)
break;
- }
mptcp_eat_recv_skb(sk, skb);
} else {
@@ -3674,6 +3669,7 @@ static int mptcp_disconnect(struct sock *sk, int flags)
mptcp_pm_data_reset(msk);
mptcp_ca_reset(sk);
msk->bytes_consumed = 0;
+ msk->copied_seq = 0;
msk->bytes_acked = 0;
msk->bytes_received = 0;
msk->bytes_sent = 0;
@@ -4601,7 +4597,7 @@ static struct sk_buff *mptcp_recv_skb(struct sock *sk, u32 *off)
mptcp_move_skbs(sk);
while ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) {
- offset = MPTCP_SKB_CB(skb)->offset;
+ offset = (u32)msk->copied_seq - MPTCP_SKB_CB(skb)->map_seq;
if (offset < skb->len) {
*off = offset;
return skb;
@@ -4643,11 +4639,9 @@ static int __mptcp_read_sock(struct sock *sk, read_descriptor_t *desc,
copied += count;
msk->bytes_consumed += count;
- if (count < data_len) {
- MPTCP_SKB_CB(skb)->offset += count;
- MPTCP_SKB_CB(skb)->map_seq += count;
+ msk->copied_seq += count;
+ if (count < data_len)
break;
- }
mptcp_eat_recv_skb(sk, skb);
if (!desc->count)
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 3be60da12924..5c1fa117e5d5 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -130,7 +130,6 @@
struct mptcp_skb_cb {
u64 map_seq;
u64 end_seq;
- u32 offset;
u8 has_rxtstamp;
u8 cant_coalesce;
};
@@ -311,6 +310,7 @@ struct mptcp_sock {
u64 rcv_data_fin_seq;
u64 bytes_retrans;
u64 bytes_consumed;
+ u64 copied_seq;
int snd_burst;
int old_wspace;
u64 recovery_snd_nxt; /* in recovery mode accept up to this seq;
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index 78e886b20258..5e553adb9397 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -533,6 +533,19 @@ static void subflow_set_remote_key(struct mptcp_sock *msk,
WRITE_ONCE(msk->ack_seq, subflow->iasn);
WRITE_ONCE(msk->can_ack, true);
atomic64_set(&msk->rcv_wnd_sent, subflow->iasn);
+ msk->copied_seq = subflow->iasn;
+
+ if (subflow->is_mptfo) {
+ struct sock *sk = (struct sock *)msk;
+ struct sk_buff *skb;
+
+ skb = skb_peek(&sk->sk_receive_queue);
+ if (skb) {
+ MPTCP_SKB_CB(skb)->map_seq = subflow->iasn - skb->len;
+ MPTCP_SKB_CB(skb)->end_seq = subflow->iasn;
+ msk->copied_seq -= skb->len;
+ }
+ }
}
static void mptcp_propagate_state(struct sock *sk, struct sock *ssk,
@@ -1485,7 +1498,7 @@ static bool subflow_check_data_avail(struct sock *ssk)
skb = skb_peek(&ssk->sk_receive_queue);
subflow->map_valid = 1;
subflow->map_data_len = skb->len;
- subflow->map_subflow_seq = tcp_sk(ssk)->copied_seq - subflow->ssn_offset;
+ subflow->map_subflow_seq = TCP_SKB_CB(skb)->seq - subflow->ssn_offset;
subflow->map_seq = __mptcp_expand_seq(subflow->map_seq,
subflow->iasn +
TCP_SKB_CB(skb)->seq -
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH mptcp-next v16 2/4] mptcp: remove CB offset field
2026-10-29 6:47 ` [PATCH mptcp-next v16 2/4] mptcp: remove CB offset field Geliang Tang
@ 2026-10-29 7:10 ` sashiko-bot
0 siblings, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-10-29 7:10 UTC (permalink / raw)
To: Geliang Tang, mptcp
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] mptcp: race and TFO progress loss in subflow_set_remote_key()
- [Critical] mptcp: stream corruption from offset subtraction in mptcp_init_skb()
- [High] mptcp: MSG_PEEK offset underflow across skbs in __mptcp_recvmsg_mskq()
- [Medium] mptcp: stale map_seq used instead of copied_seq in mptcp_inq_hint()
--
commit 285d6e793ca5e906015b250b9d8ba67747179bc7
Author: Paolo Abeni <pabeni@redhat.com>
mptcp: remove CB offset field
This commit replaces MPTCP_SKB_CB(skb)->offset with an msk-level copied_seq
field to track the sequence number of bytes already consumed by user-space
from the MPTCP receive queue.
> diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
> index 528a72da646cc..f89ed94b668d9 100644
> --- a/net/mptcp/protocol.c
> +++ b/net/mptcp/protocol.c
> @@ -169,7 +169,6 @@ static bool __mptcp_try_coalesce(struct sock *sk, struct sk_buff *to,
>
> if (MPTCP_SKB_CB(from)->map_seq != MPTCP_SKB_CB(to)->end_seq ||
> unlikely(MPTCP_SKB_CB(to)->cant_coalesce) ||
> - MPTCP_SKB_CB(from)->offset ||
> ((to->len + from->len) > (limit >> 3)) ||
> !skb_try_coalesce(to, from, fragstolen, delta))
> return false;
> @@ -416,8 +415,7 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
> skb_set_owner_r(skb, sk);
> }
>
> -static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset,
> - int copy_len)
> +static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset)
> {
> struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk);
> bool has_rxtstamp = TCP_SKB_CB(skb)->has_rxtstamp;
> @@ -426,9 +424,9 @@ static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset,
> * mptcp_subflow_get_mapped_dsn() is based on the current tp->copied_seq
> * value
> */
> - MPTCP_SKB_CB(skb)->map_seq = mptcp_subflow_get_mapped_dsn(subflow);
> - MPTCP_SKB_CB(skb)->end_seq = MPTCP_SKB_CB(skb)->map_seq + copy_len;
> - MPTCP_SKB_CB(skb)->offset = offset;
> + MPTCP_SKB_CB(skb)->map_seq = mptcp_subflow_get_mapped_dsn(subflow) -
> + offset;
> + MPTCP_SKB_CB(skb)->end_seq = MPTCP_SKB_CB(skb)->map_seq + skb->len;
[Severity: Critical]
Can subtracting offset from MPTCP_SKB_CB(skb)->map_seq here introduce a
regression that corrupts the MPTCP receive byte stream when an skb has a
non-zero subflow offset (offset = tp->copied_seq - TCP_SKB_CB(skb)->seq > 0
in __mptcp_move_skbs_from_subflow())?
For example, when an skb spans two DSS mappings and the first mapping's bytes
were discarded by mptcp_subflow_discard_data(), or when TCP queues a
partially overlapping segment with TCP_SKB_CB(skb)->seq < tp->copied_seq, the
first offset bytes of skb are discarded subflow data and the valid mapped
data starts at D = mptcp_subflow_get_mapped_dsn(subflow).
Subtracting offset assigns the DSN range [D - offset, D) to the unpulled
0..offset - 1 prefix of skb:
1. Because __mptcp_try_coalesce() no longer checks MPTCP_SKB_CB(from)->offset
and skb_try_coalesce() copies or appends the from skb starting at byte 0,
any preceding skb in msk->backlog_list, sk->sk_receive_queue, or
msk->out_of_order_queue whose end_seq equals D - offset will coalesce
with this skb and append the discarded 0..offset - 1 prefix into the
received data stream.
2. When the mapping D is out-of-order (after64(D, msk->ack_seq)),
__mptcp_move_skb() and mptcp_data_queue_ofo() use map_seq = D - offset:
- If D - offset > msk->ack_seq, mptcp_data_queue_ofo() inserts the skb at
D - offset, drops genuine incoming segments for [D - offset, D) as
duplicate data, and later delivers the stale 0..offset - 1 bytes to
user-space when msk->ack_seq reaches D - offset.
- If D - offset <= msk->ack_seq < D, __mptcp_move_skb() treats the
out-of-order skb as a partial in-sequence packet, queues it immediately
to sk->sk_receive_queue, and advances msk->ack_seq across the unreceived
gap [msk->ack_seq, D).
[ ... ]
> @@ -2195,11 +2186,17 @@ static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg,
> int copied = 0;
>
> skb_queue_walk_safe(&sk->sk_receive_queue, skb, tmp) {
> - u32 delta, offset = MPTCP_SKB_CB(skb)->offset;
> + u32 delta, offset = (u32)msk->copied_seq - MPTCP_SKB_CB(skb)->map_seq;
> u32 data_len = skb->len - offset;
> u32 count;
> int err;
>
> + /* Skip skbs that are entirely before copied_seq */
> + if (offset >= skb->len) {
> + *last = skb;
> + continue;
> + }
[Severity: High]
Does this offset calculation break recvmsg() with MSG_PEEK when peeking
across multiple skbs in sk->sk_receive_queue?
When flags & MSG_PEEK is set, msk->copied_seq is not advanced after copying
data from the first skb (skb1). When the loop moves to the second skb (skb2)
where MPTCP_SKB_CB(skb2)->map_seq >= MPTCP_SKB_CB(skb1)->end_seq >
msk->copied_seq, the unsigned subtraction:
offset = (u32)msk->copied_seq - MPTCP_SKB_CB(skb2)->map_seq;
underflows to a large u32 value, so offset >= skb->len evaluates to true and
causes __mptcp_recvmsg_mskq() to skip skb2 and all subsequent skbs in
sk->sk_receive_queue. If MSG_WAITALL or SO_RCVLOWAT requests more bytes than
skb1 contains, mptcp_recvmsg() can then hang in sk_wait_data() even though
enough unread data is already queued.
Conversely, if skb2 had a partially overlapping map_seq <= msk->copied_seq
(since __mptcp_move_skb() and __mptcp_ofo_queue() no longer advance map_seq
to msk->ack_seq), offset on skb2 will not underflow and will start copying
at msk->copied_seq instead of skb1->end_seq, duplicating the overlapping
range [msk->copied_seq, skb1->end_seq).
> +
> if (flags & MSG_PEEK) {
> /* skip already peeked skbs */
> if (total_data_len + data_len <= copied_total) {
> @@ -2233,11 +2230,9 @@ static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg,
>
> if (!(flags & MSG_PEEK)) {
> msk->bytes_consumed += count;
> - if (count < data_len) {
> - MPTCP_SKB_CB(skb)->offset += count;
> - MPTCP_SKB_CB(skb)->map_seq += count;
> + msk->copied_seq += count;
> + if (count < data_len)
> break;
> - }
[Severity: Medium]
Now that partial reads here and in __mptcp_read_sock() advance
msk->copied_seq instead of MPTCP_SKB_CB(skb)->map_seq (and overlapping skbs
no longer advance map_seq), doesn't mptcp_inq_hint() also need to be updated?
In net/mptcp/protocol.c:mptcp_inq_hint():
skb = skb_peek(&sk->sk_receive_queue);
if (skb) {
u64 hint_val = READ_ONCE(msk->ack_seq) - MPTCP_SKB_CB(skb)->map_seq;
Because MPTCP_SKB_CB(skb)->map_seq remains at the initial sequence number of
the head skb after a partial read or overlap, mptcp_inq_hint() will include
the already-consumed bytes (msk->copied_seq - MPTCP_SKB_CB(skb)->map_seq)
when reporting unread bytes for SIOCINQ, TCP_CM_INQ, and
mptcp_rcv_space_adjust(). The commit message mentions using an
ack_seq - copied_seq hint, but mptcp_inq_hint() still subtracts
MPTCP_SKB_CB(skb)->map_seq.
[ ... ]
> diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
> index 78e886b202585..5e553adb9397e 100644
> --- a/net/mptcp/subflow.c
> +++ b/net/mptcp/subflow.c
> @@ -533,6 +533,19 @@ static void subflow_set_remote_key(struct mptcp_sock *msk,
> WRITE_ONCE(msk->ack_seq, subflow->iasn);
> WRITE_ONCE(msk->can_ack, true);
> atomic64_set(&msk->rcv_wnd_sent, subflow->iasn);
> + msk->copied_seq = subflow->iasn;
> +
> + if (subflow->is_mptfo) {
> + struct sock *sk = (struct sock *)msk;
> + struct sk_buff *skb;
> +
> + skb = skb_peek(&sk->sk_receive_queue);
> + if (skb) {
> + MPTCP_SKB_CB(skb)->map_seq = subflow->iasn - skb->len;
> + MPTCP_SKB_CB(skb)->end_seq = subflow->iasn;
> + msk->copied_seq -= skb->len;
> + }
> + }
> }
[Severity: Critical]
Can this TFO synchronization in subflow_set_remote_key() race with a
concurrent reader on the msk socket and also lose partial read progress?
On a passive TFO connection, mptcp_fastopen_subflow_synack_set_params()
queues the TFO skb onto sk->sk_receive_queue with map_seq = -(u64)skb->len
and msk->copied_seq = -(u64)skb->len before the peer's 3rd ACK arrives.
When the 3rd ACK arrives in BH context, both callers of
subflow_set_remote_key():
check_fully_established()
mptcp_data_lock((struct sock *)msk);
__mptcp_subflow_fully_established(msk, subflow, mp_opt);
subflow_set_remote_key(msk, subflow, mp_opt);
mptcp_data_unlock((struct sock *)msk);
and:
mptcp_propagate_state()
mptcp_data_lock(sk);
if (mp_opt) {
...
subflow_set_remote_key(msk, subflow, mp_opt);
}
only hold mptcp_data_lock(sk) without checking sock_owned_by_user(sk).
1. Meanwhile, user-space can accept() the socket and call mptcp_recvmsg() ->
__mptcp_recvmsg_mskq() or mptcp_read_sock() -> __mptcp_read_sock() ->
mptcp_recv_skb() under lock_sock(sk) without holding mptcp_data_lock(sk).
If mptcp_eat_recv_skb() unlinks and frees the TFO skb from
sk->sk_receive_queue concurrently while subflow_set_remote_key() peeks
sk->sk_receive_queue and writes to MPTCP_SKB_CB(skb)->map_seq and end_seq,
can subflow_set_remote_key() trigger a use-after-free write on the freed
skb, or overwrite msk->copied_seq with subflow->iasn - skb->len right
before the consumed skb is unlinked?
2. Even without a concurrent race, if user-space partially reads count bytes
(0 < count < skb->len) from the TFO skb and releases lock_sock(sk) before
the 3rd ACK arrives, msk->copied_seq is advanced to
-(u64)skb->len + count, while the TFO skb remains at the head of
sk->sk_receive_queue with skb->len unchanged. When the 3rd ACK arrives,
subflow_set_remote_key() sets msk->copied_seq = subflow->iasn - skb->len,
discarding the count bytes of progress so the next read re-copies the
first count bytes to user-space and advances msk->bytes_consumed past
msk->bytes_received.
The commit message notes that IASN synchronization is delegated to
mptcp_release_cb() and __mptcp_move_skb() to preserve partial TFO progress,
but that logic does not appear to be present in the patch.
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1793255126.git.tanggeliang@kylinos.cn?part=2
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH mptcp-next v16 3/4] mptcp: sync mptcp skb cb layout with tcp one
2026-10-29 6:47 [PATCH mptcp-next v16 0/4] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
2026-10-29 6:47 ` [PATCH mptcp-next v16 1/4] mptcp: drop the mptcp_ooo_try_coalesce() helper Geliang Tang
2026-10-29 6:47 ` [PATCH mptcp-next v16 2/4] mptcp: remove CB offset field Geliang Tang
@ 2026-10-29 6:47 ` Geliang Tang
2026-10-29 7:19 ` sashiko-bot
2026-10-29 6:47 ` [PATCH mptcp-next v16 4/4] mptcp: defer read_sock cleanup to mptcp_worker Geliang Tang
2026-10-29 8:42 ` [PATCH mptcp-next v16 0/4] Reduce the differences between TCP and MPTCP for TLS usage MPTCP CI
4 siblings, 1 reply; 8+ messages in thread
From: Geliang Tang @ 2026-10-29 6:47 UTC (permalink / raw)
To: mptcp; +Cc: Paolo Abeni, Geliang Tang
From: Paolo Abeni <pabeni@redhat.com>
The MPTCP protocol uses a significantly different CB layout WRT TCP, as it
includes different information and use 64 bits for the sequence numbers.
As the msk-level rcvbuf buffer size is limited by the core socket code the
INT_MAX; after validating the incoming skb vs the current receive window,
we can safely use 32 bits for MPTCP-level sequence number. This allow
updating the MPTCP CB layout so that fields with a corresponding TCP-level
data use the same area inside the CB itself.
The 32-bit sequence numbers are safe because all SKBs in the receive queue
and OFO queue have been validated against the receive window (max INT_MAX),
so their sequence numbers are guaranteed to be within 2^31 of the current
ack_seq or copied_seq. This ensures that 32-bit arithmetic (comparisons
and subtractions) produces correct results even when 64-bit values cross
the 2^32 boundary, as the relative distance remains small.
Add build time check to ensure the latter invariant.
Co-developed-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
net/mptcp/fastopen.c | 6 ++-
net/mptcp/protocol.c | 88 +++++++++++++++++++++++++++-----------------
net/mptcp/protocol.h | 7 +++-
net/mptcp/subflow.c | 1 +
4 files changed, 64 insertions(+), 38 deletions(-)
diff --git a/net/mptcp/fastopen.c b/net/mptcp/fastopen.c
index 04b1038b5200..08f0402324c2 100644
--- a/net/mptcp/fastopen.c
+++ b/net/mptcp/fastopen.c
@@ -48,8 +48,10 @@ void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subf
/* The TFO segment data sits before the IASN; before receiving
* the remote key, IASN is assumed being 0.
*/
- MPTCP_SKB_CB(skb)->map_seq = -(u64)skb->len;
+ MPTCP_SKB_CB(skb)->map_seq64 = -(u64)skb->len;
+ MPTCP_SKB_CB(skb)->map_seq = MPTCP_SKB_CB(skb)->map_seq64;
MPTCP_SKB_CB(skb)->end_seq = 0;
+ MPTCP_SKB_CB(skb)->flags = 0;
MPTCP_SKB_CB(skb)->has_rxtstamp = has_rxtstamp;
MPTCP_SKB_CB(skb)->cant_coalesce = 1;
@@ -57,7 +59,7 @@ void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subf
DEBUG_NET_WARN_ON_ONCE(sock_owned_by_user_nocheck(sk));
msk = mptcp_sk(sk);
- msk->copied_seq = MPTCP_SKB_CB(skb)->map_seq;
+ msk->copied_seq = MPTCP_SKB_CB(skb)->map_seq64;
mptcp_borrow_fwdmem(sk, skb);
skb_set_owner_r(skb, sk);
__skb_queue_tail(&sk->sk_receive_queue, skb);
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index f89ed94b668d..bbce1bafa40e 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -168,12 +168,14 @@ static bool __mptcp_try_coalesce(struct sock *sk, struct sk_buff *to,
int limit = READ_ONCE(sk->sk_rcvbuf);
if (MPTCP_SKB_CB(from)->map_seq != MPTCP_SKB_CB(to)->end_seq ||
+ MPTCP_SKB_CB(from)->map_seq64 != MPTCP_SKB_CB(to)->map_seq64 +
+ to->len ||
unlikely(MPTCP_SKB_CB(to)->cant_coalesce) ||
((to->len + from->len) > (limit >> 3)) ||
!skb_try_coalesce(to, from, fragstolen, delta))
return false;
- pr_debug("colesced seq %llx into %llx new len %d new end seq %llx\n",
+ pr_debug("colesced seq %x into %x new len %d new end seq %x\n",
MPTCP_SKB_CB(from)->map_seq, MPTCP_SKB_CB(to)->map_seq,
to->len, MPTCP_SKB_CB(from)->end_seq);
MPTCP_SKB_CB(to)->end_seq = MPTCP_SKB_CB(from)->end_seq;
@@ -255,8 +257,8 @@ static void mptcp_prune_ofo_queue(struct sock *sk,
struct sk_buff *skb = rb_to_skb(node);
/* Stop pruning if the incoming skb would land in OoO tail. */
- if (after64(MPTCP_SKB_CB(in_skb)->map_seq,
- MPTCP_SKB_CB(skb)->map_seq))
+ if (after(MPTCP_SKB_CB(in_skb)->map_seq,
+ MPTCP_SKB_CB(skb)->map_seq))
break;
pruned = true;
@@ -302,16 +304,21 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
{
struct sock *sk = (struct sock *)msk;
struct rb_node **p, *parent;
- u64 seq, end_seq, max_seq;
+ u64 end_seq, max_seq;
struct sk_buff *skb1;
+ u32 seq;
seq = MPTCP_SKB_CB(skb)->map_seq;
- end_seq = MPTCP_SKB_CB(skb)->end_seq;
+ end_seq = MPTCP_SKB_CB(skb)->map_seq64 + skb->len;
max_seq = atomic64_read(&msk->rcv_wnd_sent);
- pr_debug("msk=%p seq=%llx limit=%llx empty=%d\n", msk, seq, max_seq,
+ pr_debug("msk=%p seq=%x limit=%llx empty=%d\n", msk, seq, max_seq,
RB_EMPTY_ROOT(&msk->out_of_order_queue));
- if (after64(end_seq, max_seq)) {
+ /* Use the full sequence space to perform the admission checks, to
+ * protect vs possible wrap-arounds.
+ */
+ if (after64(MPTCP_SKB_CB(skb)->map_seq64, max_seq) ||
+ after64(end_seq, max_seq)) {
/* out of window */
mptcp_drop(sk, skb);
pr_debug("oow by %lld, rcv_wnd_sent %llu\n",
@@ -346,7 +353,7 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
}
/* Can avoid an rbtree lookup if we are adding skb after ooo_last_skb */
- if (!before64(seq, MPTCP_SKB_CB(msk->ooo_last_skb)->end_seq)) {
+ if (!before(seq, MPTCP_SKB_CB(msk->ooo_last_skb)->end_seq)) {
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_OFOQUEUETAIL);
parent = &msk->ooo_last_skb->rbnode;
p = &parent->rb_right;
@@ -358,18 +365,18 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
while (*p) {
parent = *p;
skb1 = rb_to_skb(parent);
- if (before64(seq, MPTCP_SKB_CB(skb1)->map_seq)) {
+ if (before(seq, MPTCP_SKB_CB(skb1)->map_seq)) {
p = &parent->rb_left;
continue;
}
- if (before64(seq, MPTCP_SKB_CB(skb1)->end_seq)) {
- if (!after64(end_seq, MPTCP_SKB_CB(skb1)->end_seq)) {
+ if (before(seq, MPTCP_SKB_CB(skb1)->end_seq)) {
+ if (!after((u32)end_seq, MPTCP_SKB_CB(skb1)->end_seq)) {
/* All the bits are present. Drop. */
mptcp_drop(sk, skb);
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA);
return;
}
- if (after64(seq, MPTCP_SKB_CB(skb1)->map_seq)) {
+ if (after(seq, MPTCP_SKB_CB(skb1)->map_seq)) {
/* partial overlap:
* | skb |
* | skb1 |
@@ -400,7 +407,7 @@ static void mptcp_data_queue_ofo(struct mptcp_sock *msk, struct sk_buff *skb)
merge_right:
/* Remove other segments covered by skb. */
while ((skb1 = skb_rb_next(skb)) != NULL) {
- if (before64(end_seq, MPTCP_SKB_CB(skb1)->end_seq))
+ if (before((u32)end_seq, MPTCP_SKB_CB(skb1)->end_seq))
break;
rb_erase(&skb1->rbnode, &msk->out_of_order_queue);
mptcp_drop(sk, skb1);
@@ -422,11 +429,13 @@ static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset)
/* the skb map_seq accounts for the skb offset:
* mptcp_subflow_get_mapped_dsn() is based on the current tp->copied_seq
- * value
+ * value; note that end seq number is only available in 32bits format.
*/
- MPTCP_SKB_CB(skb)->map_seq = mptcp_subflow_get_mapped_dsn(subflow) -
- offset;
+ MPTCP_SKB_CB(skb)->map_seq64 = mptcp_subflow_get_mapped_dsn(subflow) -
+ offset;
+ MPTCP_SKB_CB(skb)->map_seq = (u32)MPTCP_SKB_CB(skb)->map_seq64;
MPTCP_SKB_CB(skb)->end_seq = MPTCP_SKB_CB(skb)->map_seq + skb->len;
+ MPTCP_SKB_CB(skb)->flags = 0;
MPTCP_SKB_CB(skb)->has_rxtstamp = has_rxtstamp;
MPTCP_SKB_CB(skb)->cant_coalesce = 0;
@@ -438,13 +447,13 @@ static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset)
static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
{
- u64 copy_len = MPTCP_SKB_CB(skb)->end_seq - MPTCP_SKB_CB(skb)->map_seq;
+ u32 copy_len = MPTCP_SKB_CB(skb)->end_seq - MPTCP_SKB_CB(skb)->map_seq;
struct mptcp_sock *msk = mptcp_sk(sk);
struct sk_buff *tail;
mptcp_borrow_fwdmem(sk, skb);
- if (MPTCP_SKB_CB(skb)->map_seq == msk->ack_seq) {
+ if (MPTCP_SKB_CB(skb)->map_seq64 == msk->ack_seq) {
/* in sequence */
insert:
if (!mptcp_try_rmem_schedule(sk, skb)) {
@@ -462,14 +471,14 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
skb_set_owner_r(skb, sk);
__skb_queue_tail(&sk->sk_receive_queue, skb);
return true;
- } else if (after64(MPTCP_SKB_CB(skb)->map_seq, msk->ack_seq)) {
+ } else if (after64(MPTCP_SKB_CB(skb)->map_seq64, msk->ack_seq)) {
mptcp_data_queue_ofo(msk, skb);
return false;
}
/* Partial packet */
- if (after64(MPTCP_SKB_CB(skb)->end_seq, msk->ack_seq)) {
- copy_len = MPTCP_SKB_CB(skb)->end_seq - msk->ack_seq;
+ if (after64(MPTCP_SKB_CB(skb)->map_seq64 + skb->len, msk->ack_seq)) {
+ copy_len = MPTCP_SKB_CB(skb)->end_seq - (u32)msk->ack_seq;
goto insert;
}
@@ -864,39 +873,37 @@ static bool __mptcp_ofo_queue(struct mptcp_sock *msk)
{
struct sock *sk = (struct sock *)msk;
struct sk_buff *skb, *tail;
+ u32 seq_delta, ack_seq;
bool moved = false;
struct rb_node *p;
- u64 end_seq;
p = rb_first(&msk->out_of_order_queue);
pr_debug("msk=%p empty=%d\n", msk, RB_EMPTY_ROOT(&msk->out_of_order_queue));
while (p) {
+ ack_seq = msk->ack_seq;
skb = rb_to_skb(p);
- if (after64(MPTCP_SKB_CB(skb)->map_seq, msk->ack_seq))
+ if (after(MPTCP_SKB_CB(skb)->map_seq, ack_seq))
break;
p = rb_next(p);
rb_erase(&skb->rbnode, &msk->out_of_order_queue);
- if (unlikely(!after64(MPTCP_SKB_CB(skb)->end_seq,
- msk->ack_seq))) {
+ if (unlikely(!after(MPTCP_SKB_CB(skb)->end_seq, ack_seq))) {
mptcp_drop(sk, skb);
MPTCP_INC_STATS(sock_net(sk), MPTCP_MIB_DUPDATA);
continue;
}
- end_seq = MPTCP_SKB_CB(skb)->end_seq;
+ seq_delta = MPTCP_SKB_CB(skb)->end_seq - ack_seq;
tail = skb_peek_tail(&sk->sk_receive_queue);
if (!tail || !mptcp_try_coalesce(sk, tail, skb)) {
- int delta = msk->ack_seq - MPTCP_SKB_CB(skb)->map_seq;
-
- pr_debug("uncoalesced seq=%llx ack seq=%llx delta=%d\n",
- MPTCP_SKB_CB(skb)->map_seq, msk->ack_seq,
- delta);
+ pr_debug("uncoalesced seq=%x ack seq=%x delta=%d\n",
+ MPTCP_SKB_CB(skb)->map_seq, ack_seq,
+ ack_seq - MPTCP_SKB_CB(skb)->map_seq);
__skb_queue_tail(&sk->sk_receive_queue, skb);
}
- msk->bytes_received += end_seq - msk->ack_seq;
- WRITE_ONCE(msk->ack_seq, end_seq);
+ msk->bytes_received += seq_delta;
+ WRITE_ONCE(msk->ack_seq, msk->ack_seq + seq_delta);
moved = true;
}
return moved;
@@ -2392,7 +2399,8 @@ static unsigned int mptcp_inq_hint(const struct sock *sk)
skb = skb_peek(&sk->sk_receive_queue);
if (skb) {
- u64 hint_val = READ_ONCE(msk->ack_seq) - MPTCP_SKB_CB(skb)->map_seq;
+ u64 hint_val = READ_ONCE(msk->ack_seq) -
+ MPTCP_SKB_CB(skb)->map_seq64;
if (hint_val >= INT_MAX)
return INT_MAX;
@@ -4847,11 +4855,23 @@ static int mptcp_napi_poll(struct napi_struct *napi, int budget)
return work_done;
}
+#define CHK_CB_FIELD(mptcp_field, tcp_field) \
+ ({ \
+ BUILD_BUG_ON(offsetof(struct mptcp_skb_cb, mptcp_field) != \
+ offsetof(struct tcp_skb_cb, tcp_field)); \
+ BUILD_BUG_ON(offsetofend(struct mptcp_skb_cb, mptcp_field) != \
+ offsetofend(struct tcp_skb_cb, tcp_field)); \
+ })
+
void __init mptcp_proto_init(void)
{
struct mptcp_delegated_action *delegated;
int cpu;
+ CHK_CB_FIELD(map_seq, seq);
+ CHK_CB_FIELD(end_seq, end_seq);
+ CHK_CB_FIELD(flags, tcp_flags);
+
mptcp_prot.h.hashinfo = tcp_prot.h.hashinfo;
if (percpu_counter_init(&mptcp_sockets_allocated, 0, GFP_KERNEL))
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 5c1fa117e5d5..2ab8342e198d 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -128,10 +128,13 @@
#define MPTCP_SYNC_SNDBUF 7
struct mptcp_skb_cb {
- u64 map_seq;
- u64 end_seq;
+ u32 map_seq;
+ u32 end_seq;
+ u32 unused;
+ u16 flags;
u8 has_rxtstamp;
u8 cant_coalesce;
+ u64 map_seq64;
};
#define MPTCP_SKB_CB(__skb) ((struct mptcp_skb_cb *)&((__skb)->cb[0]))
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
index 5e553adb9397..d144eb2373ef 100644
--- a/net/mptcp/subflow.c
+++ b/net/mptcp/subflow.c
@@ -542,6 +542,7 @@ static void subflow_set_remote_key(struct mptcp_sock *msk,
skb = skb_peek(&sk->sk_receive_queue);
if (skb) {
MPTCP_SKB_CB(skb)->map_seq = subflow->iasn - skb->len;
+ MPTCP_SKB_CB(skb)->map_seq64 = subflow->iasn - skb->len;
MPTCP_SKB_CB(skb)->end_seq = subflow->iasn;
msk->copied_seq -= skb->len;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH mptcp-next v16 3/4] mptcp: sync mptcp skb cb layout with tcp one
2026-10-29 6:47 ` [PATCH mptcp-next v16 3/4] mptcp: sync mptcp skb cb layout with tcp one Geliang Tang
@ 2026-10-29 7:19 ` sashiko-bot
0 siblings, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-10-29 7:19 UTC (permalink / raw)
To: Geliang Tang, mptcp
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] mptcp: race and copied_seq reset in subflow_set_remote_key() on TFO
- [Critical] mptcp: mptcp_init_skb() subtracts offset from map_seq64 corrupting data
- [High] mptcp: __mptcp_recvmsg_mskq() offset underflow on MSG_PEEK and skb leak
- [Medium] mptcp: mptcp_inq_hint() uses map_seq64 instead of msk->copied_seq
--
commit 67fabb0bf5f4c5ce5c4d525d8c3f0296f5ef7122
Author: Paolo Abeni <pabeni@redhat.com>
mptcp: sync mptcp skb cb layout with tcp one
This commit aligns the MPTCP SKB control buffer layout with the TCP SKB
control buffer by converting map_seq and end_seq to 32-bit fields while
retaining a 64-bit map_seq64 field for sequence validation.
> diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
> index f89ed94b668d9..bbce1bafa40ee 100644
> --- a/net/mptcp/protocol.c
> +++ b/net/mptcp/protocol.c
[ ... ]
> @@ -422,11 +429,13 @@ static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset)
>
> /* the skb map_seq accounts for the skb offset:
> * mptcp_subflow_get_mapped_dsn() is based on the current tp->copied_seq
> - * value
> + * value; note that end seq number is only available in 32bits format.
> */
> - MPTCP_SKB_CB(skb)->map_seq = mptcp_subflow_get_mapped_dsn(subflow) -
> - offset;
> + MPTCP_SKB_CB(skb)->map_seq64 = mptcp_subflow_get_mapped_dsn(subflow) -
> + offset;
> + MPTCP_SKB_CB(skb)->map_seq = (u32)MPTCP_SKB_CB(skb)->map_seq64;
> MPTCP_SKB_CB(skb)->end_seq = MPTCP_SKB_CB(skb)->map_seq + skb->len;
[Severity: Critical]
Can subtracting offset from mptcp_subflow_get_mapped_dsn(subflow) in
mptcp_init_skb() corrupt the MPTCP data stream when an out-of-order DSS
mapping starts at a non-zero offset within a subflow SKB?
In __mptcp_move_skbs_from_subflow(), offset = seq - TCP_SKB_CB(skb)->seq
(where seq is tp->copied_seq) is non-zero when the leading [0, offset)
bytes of the subflow SKB were already consumed on the subflow. For example,
when an SKB spans the end of a previous DSS mapping that was already
acknowledged, subflow_check_data_avail() -> mptcp_subflow_discard_data()
advances tp->copied_seq without eating the SKB and clears
subflow->map_valid, after which get_mapping_status() installs the next
mapping on the remaining [offset, skb->len) bytes:
net/mptcp/subflow.c:subflow_check_data_avail()
if (unlikely(before64(ack_seq, old_ack))) {
mptcp_subflow_discard_data(ssk, skb, old_ack - ack_seq);
continue;
}
net/mptcp/subflow.c:mptcp_subflow_discard_data()
if (!before(tcp_sk(ssk)->copied_seq, TCP_SKB_CB(skb)->end_seq))
sk_eat_skb(ssk, skb);
if (mptcp_subflow_get_map_offset(subflow) >= subflow->map_data_len)
subflow->map_valid = 0;
The active DSS mapping only covers skb[offset .. skb->len - 1] starting at
mapped_dsn = mptcp_subflow_get_mapped_dsn(subflow). However,
mptcp_init_skb() sets map_seq64 = mapped_dsn - offset, assigning the MPTCP
sequence range [mapped_dsn - offset, mapped_dsn) to the discarded prefix
bytes skb[0 .. offset - 1].
When mapped_dsn is out of order at the MPTCP level (after64(mapped_dsn,
msk->ack_seq), because the intermediate DSNs were sent on another subflow),
__mptcp_move_skb(), mptcp_data_queue_ofo(), __mptcp_ofo_queue(), and
__mptcp_try_coalesce() treat skb[0 .. offset - 1] as valid payload for
[mapped_dsn - offset, mapped_dsn). Doesn't this advance msk->ack_seq across
that range, deliver the stale subflow prefix bytes to user space, and drop
the actual segment for [mapped_dsn - offset, mapped_dsn) as duplicate data?
> + MPTCP_SKB_CB(skb)->flags = 0;
> MPTCP_SKB_CB(skb)->has_rxtstamp = has_rxtstamp;
> MPTCP_SKB_CB(skb)->cant_coalesce = 0;
>
> @@ -438,13 +447,13 @@ static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset)
>
> static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
> {
> - u64 copy_len = MPTCP_SKB_CB(skb)->end_seq - MPTCP_SKB_CB(skb)->map_seq;
> + u32 copy_len = MPTCP_SKB_CB(skb)->end_seq - MPTCP_SKB_CB(skb)->map_seq;
> struct mptcp_sock *msk = mptcp_sk(sk);
> struct sk_buff *tail;
>
> mptcp_borrow_fwdmem(sk, skb);
>
> - if (MPTCP_SKB_CB(skb)->map_seq == msk->ack_seq) {
> + if (MPTCP_SKB_CB(skb)->map_seq64 == msk->ack_seq) {
> /* in sequence */
> insert:
> if (!mptcp_try_rmem_schedule(sk, skb)) {
> @@ -462,14 +471,14 @@ static bool __mptcp_move_skb(struct sock *sk, struct sk_buff *skb)
> skb_set_owner_r(skb, sk);
> __skb_queue_tail(&sk->sk_receive_queue, skb);
> return true;
> - } else if (after64(MPTCP_SKB_CB(skb)->map_seq, msk->ack_seq)) {
> + } else if (after64(MPTCP_SKB_CB(skb)->map_seq64, msk->ack_seq)) {
> mptcp_data_queue_ofo(msk, skb);
> return false;
> }
>
> /* Partial packet */
> - if (after64(MPTCP_SKB_CB(skb)->end_seq, msk->ack_seq)) {
> - copy_len = MPTCP_SKB_CB(skb)->end_seq - msk->ack_seq;
> + if (after64(MPTCP_SKB_CB(skb)->map_seq64 + skb->len, msk->ack_seq)) {
> + copy_len = MPTCP_SKB_CB(skb)->end_seq - (u32)msk->ack_seq;
> goto insert;
> }
[Severity: High]
With MPTCP_SKB_CB(skb)->map_seq now a 32-bit value and msk->copied_seq
tracking read progress, does __mptcp_recvmsg_mskq() underflow offset during
MSG_PEEK and leak fully skipped SKBs on non-peek reads?
In net/mptcp/protocol.c:__mptcp_recvmsg_mskq():
skb_queue_walk_safe(&sk->sk_receive_queue, skb, tmp) {
u32 delta, offset = (u32)msk->copied_seq - MPTCP_SKB_CB(skb)->map_seq;
u32 data_len = skb->len - offset;
u32 count;
int err;
/* Skip skbs that are entirely before copied_seq */
if (offset >= skb->len) {
*last = skb;
continue;
}
When flags & MSG_PEEK is set, msk->copied_seq is not advanced after copying
from the first SKB in sk->sk_receive_queue. On the second SKB,
MPTCP_SKB_CB(skb)->map_seq is after (u32)msk->copied_seq, so the unsigned
32-bit subtraction (u32)msk->copied_seq - MPTCP_SKB_CB(skb)->map_seq wraps
around to a value >= 0x80000000U (>= skb->len). This hits the
offset >= skb->len check and skips every SKB after the first, preventing
MSG_PEEK from ever reading past the first SKB (and causing MSG_PEEK |
MSG_WAITALL to block in sk_wait_data() even when enough data is queued).
Additionally, when !(flags & MSG_PEEK) and an SKB in sk->sk_receive_queue
is genuinely before msk->copied_seq (offset >= skb->len),
__mptcp_recvmsg_mskq() executes *last = skb; continue; without calling
mptcp_eat_recv_skb(sk, skb) (unlike mptcp_recv_skb()). Doesn't that leave
the consumed SKB leaked on sk->sk_receive_queue and charged to
sk->sk_rmem_alloc until the socket is closed?
[ ... ]
> @@ -2392,7 +2399,8 @@ static unsigned int mptcp_inq_hint(const struct sock *sk)
>
> skb = skb_peek(&sk->sk_receive_queue);
> if (skb) {
> - u64 hint_val = READ_ONCE(msk->ack_seq) - MPTCP_SKB_CB(skb)->map_seq;
> + u64 hint_val = READ_ONCE(msk->ack_seq) -
> + MPTCP_SKB_CB(skb)->map_seq64;
>
> if (hint_val >= INT_MAX)
> return INT_MAX;
[Severity: Medium]
Should mptcp_inq_hint() subtract msk->copied_seq instead of
MPTCP_SKB_CB(skb)->map_seq64?
When user space partially reads the head SKB in sk->sk_receive_queue via
__mptcp_recvmsg_mskq() or __mptcp_read_sock() (or when a partially
overlapping SKB is queued in __mptcp_move_skb() or __mptcp_ofo_queue()),
msk->copied_seq advances while MPTCP_SKB_CB(skb)->map_seq64 on the head SKB
remains unchanged at its initial sequence number:
net/mptcp/protocol.c:__mptcp_recvmsg_mskq()
if (!(flags & MSG_PEEK)) {
msk->bytes_consumed += count;
msk->copied_seq += count;
if (count < data_len)
break;
Computing READ_ONCE(msk->ack_seq) - MPTCP_SKB_CB(skb)->map_seq64 counts the
already-consumed or skipped prefix bytes on the head SKB (including when
subsequent segments coalesce into that head SKB via mptcp_try_coalesce())
as unread.
Beyond reporting inflated unread byte counts for SIOCINQ in mptcp_ioctl()
and TCP_CM_INQ in mptcp_recvmsg(), mptcp_rcv_space_adjust() subtracts
mptcp_inq_hint(sk) from msk->rcvq_space.copied:
net/mptcp/protocol.c:mptcp_rcv_space_adjust()
copied = msk->rcvq_space.copied;
copied -= mptcp_inq_hint(sk);
if (copied <= msk->rcvq_space.space)
goto new_measure;
Won't subtracting the inflated hint_val cause copied to be under-counted
and prevent mptcp_rcvbuf_grow() from auto-tuning the receive buffer?
> diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
> index 5e553adb9397e..d144eb2373ef6 100644
> --- a/net/mptcp/subflow.c
> +++ b/net/mptcp/subflow.c
> @@ -542,6 +542,7 @@ static void subflow_set_remote_key(struct mptcp_sock *msk,
> skb = skb_peek(&sk->sk_receive_queue);
> if (skb) {
> MPTCP_SKB_CB(skb)->map_seq = subflow->iasn - skb->len;
> + MPTCP_SKB_CB(skb)->map_seq64 = subflow->iasn - skb->len;
> MPTCP_SKB_CB(skb)->end_seq = subflow->iasn;
> msk->copied_seq -= skb->len;
> }
[Severity: Critical]
What happens on passive MPTCP Fast Open if user space accepts the socket
and reads from sk->sk_receive_queue before or while the client's 3rd ACK
triggers subflow_set_remote_key()?
In mptcp_fastopen_subflow_synack_set_params(), the TFO data SKB is queued
onto sk->sk_receive_queue with map_seq64 = -(u64)skb->len and
msk->copied_seq = -(u64)skb->len before the 3rd ACK arrives. Once the child
socket is accepted, user space can call mptcp_recvmsg() ->
__mptcp_recvmsg_mskq() (or __mptcp_read_sock() -> mptcp_recv_skb()) under
lock_sock(sk) without holding mptcp_data_lock(sk), while the 3rd ACK
arrives in softirq via check_fully_established() ->
__mptcp_subflow_fully_established() -> subflow_set_remote_key() (or
mptcp_propagate_state() -> subflow_set_remote_key()) under
mptcp_data_lock(sk) without checking sock_owned_by_user(sk):
net/mptcp/subflow.c:subflow_set_remote_key()
msk->copied_seq = subflow->iasn;
if (subflow->is_mptfo) {
struct sock *sk = (struct sock *)msk;
struct sk_buff *skb;
skb = skb_peek(&sk->sk_receive_queue);
if (skb) {
MPTCP_SKB_CB(skb)->map_seq = subflow->iasn - skb->len;
MPTCP_SKB_CB(skb)->map_seq64 = subflow->iasn - skb->len;
MPTCP_SKB_CB(skb)->end_seq = subflow->iasn;
msk->copied_seq -= skb->len;
}
}
This appears to cause three regressions:
1. If user space partially reads count bytes (0 < count < skb->len) from
the TFO SKB before the 3rd ACK arrives, __mptcp_recvmsg_mskq() advances
msk->copied_seq to -(u64)skb->len + count while leaving the SKB (with
skb->len unchanged) at the head of sk->sk_receive_queue. When
subflow_set_remote_key() runs, it sets msk->copied_seq to
subflow->iasn - skb->len, discarding the partial read progress so the next
recvmsg() re-delivers the first count bytes.
2. In __mptcp_recvmsg_mskq() and mptcp_recv_skb(), mptcp_eat_recv_skb()
unlinks the TFO SKB from sk->sk_receive_queue and frees it via
skb_attempt_defer_free(skb) under lock_sock(sk) without mptcp_data_lock(sk).
If mptcp_eat_recv_skb() unlinks and frees skb right after
subflow_set_remote_key() peeks it with skb_peek(&sk->sk_receive_queue),
can't subflow_set_remote_key() perform a use-after-free write to
MPTCP_SKB_CB(skb)?
3. In __mptcp_recvmsg_mskq(), msk->copied_seq += count runs before
mptcp_eat_recv_skb(sk, skb). If subflow_set_remote_key() executes between
those two lines when the TFO SKB is completely read, skb_peek() still sees
the SKB and sets msk->copied_seq = subflow->iasn - skb->len before
mptcp_eat_recv_skb() unlinks it, leaving msk->copied_seq permanently behind
msk->ack_seq by skb->len and causing subsequent SKBs to underflow offset
and be skipped.
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1793255126.git.tanggeliang@kylinos.cn?part=3
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH mptcp-next v16 4/4] mptcp: defer read_sock cleanup to mptcp_worker
2026-10-29 6:47 [PATCH mptcp-next v16 0/4] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
` (2 preceding siblings ...)
2026-10-29 6:47 ` [PATCH mptcp-next v16 3/4] mptcp: sync mptcp skb cb layout with tcp one Geliang Tang
@ 2026-10-29 6:47 ` Geliang Tang
2026-10-29 8:42 ` [PATCH mptcp-next v16 0/4] Reduce the differences between TCP and MPTCP for TLS usage MPTCP CI
4 siblings, 0 replies; 8+ messages in thread
From: Geliang Tang @ 2026-10-29 6:47 UTC (permalink / raw)
To: mptcp; +Cc: Paolo Abeni, Geliang Tang
From: Paolo Abeni <pabeni@redhat.com>
When MPTCP carries TLS, __mptcp_read_sock() is reached from BH context
(via tls_strp_read_copyin() -> sk->sk_read_sock()), where calling
mptcp_rcv_space_adjust() and mptcp_cleanup_rbuf() is not safe as both
expect process context.
Move both calls to mptcp_worker(): __mptcp_read_sock() accumulates the
consumed bytes in msk->read_copied and signals the worker through a new
MPTCP_WORK_READ_COMPLETE bit. The worker drains the accumulator under
mptcp_data_lock() and runs rcvbuf autotuning and RBU update in process
context.
Also remove the backlog flush from mptcp_recv_skb() (covered by
mptcp_release_cb()), drop the msk_owned_by_me() assertion (BH caller
does not own the msk lock), and drop the redundant mptcp_recv_skb()
call after the main loop.
Initialize msk->read_copied in __mptcp_init_sock() and reset it in
mptcp_disconnect().
Co-developed-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Geliang Tang <geliang@kernel.org>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
---
net/mptcp/protocol.c | 37 ++++++++++++++++++++++++++++---------
net/mptcp/protocol.h | 2 ++
2 files changed, 30 insertions(+), 9 deletions(-)
diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
index bbce1bafa40e..322d7a5ba0b8 100644
--- a/net/mptcp/protocol.c
+++ b/net/mptcp/protocol.c
@@ -3121,6 +3121,21 @@ static void mptcp_backlog_purge(struct sock *sk)
sk_mem_reclaim(sk);
}
+static void mptcp_read_complete(struct sock *sk)
+{
+ struct mptcp_sock *msk = mptcp_sk(sk);
+ int read_copied;
+
+ mptcp_data_lock(sk);
+ read_copied = msk->read_copied;
+ msk->read_copied = 0;
+ mptcp_data_unlock(sk);
+
+ mptcp_rcv_space_adjust(msk, read_copied);
+ if (read_copied > 0)
+ mptcp_cleanup_rbuf(msk, read_copied);
+}
+
static void mptcp_do_fastclose(struct sock *sk)
{
struct mptcp_subflow_context *subflow, *tmp;
@@ -3196,6 +3211,9 @@ static void mptcp_worker(struct work_struct *work)
if (test_and_clear_bit(MPTCP_WORK_RTX, &msk->flags))
__mptcp_retrans(sk);
+ if (test_and_clear_bit(MPTCP_WORK_READ_COMPLETE, &msk->flags))
+ mptcp_read_complete(sk);
+
fail_tout = msk->first ? READ_ONCE(mptcp_subflow_ctx(msk->first)->fail_tout) : 0;
if (fail_tout && time_after(jiffies, fail_tout))
mptcp_mp_fail_no_response(msk);
@@ -3239,6 +3257,7 @@ static void __mptcp_init_sock(struct sock *sk)
msk->last_data_sent = tcp_jiffies32;
msk->last_data_recv = tcp_jiffies32;
msk->last_ack_recv = tcp_jiffies32;
+ msk->read_copied = 0;
mptcp_pm_data_init(msk);
spin_lock_init(&msk->fallback_lock);
@@ -3684,6 +3703,7 @@ static int mptcp_disconnect(struct sock *sk, int flags)
msk->bytes_retrans = 0;
msk->rcvspace_init = 0;
msk->fastclosing = 0;
+ msk->read_copied = 0;
mptcp_init_rtt_est(msk);
/* for fallback's sake */
@@ -4601,9 +4621,6 @@ static struct sk_buff *mptcp_recv_skb(struct sock *sk, u32 *off)
struct sk_buff *skb;
u32 offset;
- if (!list_empty(&msk->backlog_list))
- mptcp_move_skbs(sk);
-
while ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) {
offset = (u32)msk->copied_seq - MPTCP_SKB_CB(skb)->map_seq;
if (offset < skb->len) {
@@ -4618,6 +4635,7 @@ static struct sk_buff *mptcp_recv_skb(struct sock *sk, u32 *off)
/*
* Note:
* - It is assumed that the socket was locked by the caller.
+ * - Can be invoked in BH scope.
*/
static int __mptcp_read_sock(struct sock *sk, read_descriptor_t *desc,
sk_read_actor_t recv_actor, bool noack)
@@ -4627,8 +4645,6 @@ static int __mptcp_read_sock(struct sock *sk, read_descriptor_t *desc,
int copied = 0;
u32 offset;
- msk_owned_by_me(msk);
-
if (sk->sk_state == TCP_LISTEN)
return -ENOTCONN;
while ((skb = mptcp_recv_skb(sk, &offset)) != NULL) {
@@ -4659,11 +4675,14 @@ static int __mptcp_read_sock(struct sock *sk, read_descriptor_t *desc,
if (noack)
goto out;
- mptcp_rcv_space_adjust(msk, copied);
-
+ /* The backlog flushing is only needed when some data is actually
+ * moved and will take place in mptcp_release_cb() via
+ * release_sock() in the worker.
+ */
if (copied > 0) {
- mptcp_recv_skb(sk, &offset);
- mptcp_cleanup_rbuf(msk, copied);
+ msk->read_copied += copied;
+ set_bit(MPTCP_WORK_READ_COMPLETE, &msk->flags);
+ mptcp_schedule_work(sk);
}
out:
return copied;
diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h
index 2ab8342e198d..b0082e1c83f0 100644
--- a/net/mptcp/protocol.h
+++ b/net/mptcp/protocol.h
@@ -117,6 +117,7 @@
#define MPTCP_FALLBACK_DONE 2
#define MPTCP_WORK_CLOSE_SUBFLOW 3
#define MPTCP_RTX_ENABLED 4
+#define MPTCP_WORK_READ_COMPLETE 5
/* MPTCP socket release cb flags */
#define MPTCP_PUSH_PENDING 1
@@ -326,6 +327,7 @@ struct mptcp_sock {
u32 last_data_sent;
u32 last_data_recv;
u32 last_ack_recv;
+ int read_copied;
unsigned long timer_ival;
u32 token;
unsigned long flags;
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH mptcp-next v16 0/4] Reduce the differences between TCP and MPTCP for TLS usage
2026-10-29 6:47 [PATCH mptcp-next v16 0/4] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
` (3 preceding siblings ...)
2026-10-29 6:47 ` [PATCH mptcp-next v16 4/4] mptcp: defer read_sock cleanup to mptcp_worker Geliang Tang
@ 2026-10-29 8:42 ` MPTCP CI
4 siblings, 0 replies; 8+ messages in thread
From: MPTCP CI @ 2026-10-29 8:42 UTC (permalink / raw)
To: Paolo Abeni; +Cc: mptcp
Hi Paolo,
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): Unstable: 2 failed test(s): packetdrill_syscalls selftest_mptcp_sockopt ⚠️
- 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: Unstable: 1 failed test(s): validation: - Critical: mptcp-perf/01-Test_1/client.log: Global Timeout - Critical: mptcp-perf/01-Test_1/server.log: Global Timeout - Critical: sched-baseline/01-slow_primary/client.log: Global Timeout - Critical: sched-baseline/01-slow_primary/server.log: Global Timeout - Critical: sched-baseline/02-tiny_primary/client.log: Global Timeout - Critical: sched-baseline/02-tiny_primary/server.log: Global Timeout - Critical: sched-baseline/03-fast_both/client.log: Global Timeout - Critical: sched-baseline/03-fast_both/server.log: Global Timeout - Critical: sched-baseline/04-balanced/client.log: Global Timeout - Critical: sched-baseline/04-balanced/server.log: Global Timeout - Critical: sched-baseline/05-slow_near_primary/client.log: Global Timeout - Critical: sched-baseline/05-slow_near_primary/server.log: Global Timeout - Critical: sched-baseline/06-slow_secondary/client.log: Global Timeout - Critical: sched-baseline/06-slow_secondary/server.log: Global Timeout - Critical: sched-baseline/07-very_fast/client.log: Global Timeout - Critical: sched-baseline/07-very_fast/server.log: Global Timeout - Critical: sched-baseline/08-shallow_primary/client.log: Global Timeout - Critical: sched-baseline/08-shallow_primary/server.log: Global Timeout - Critical: sched-baseline/09-high_lat_primary/client.log: Global Timeout - Critical: sched-baseline/09-high_lat_primary/server.log: Global Timeout - Critical: sched-baseline/10-huge_lat_primary/client.log: Global Timeout - Critical: sched-baseline/10-huge_lat_primary/server.log: Global Timeout - Critical: sched-baseline/11-high_lat_secondary/client.log: Global Timeout - Critical: sched-baseline/11-high_lat_secondary/server.log: Global Timeout - Critical: sched-baseline/12-huge_lat_secondary/client.log: Global Timeout - Critical: sched-baseline/12-huge_lat_secondary/server.log: Global Timeout - Critical: sched-baseline/13-bloated_primary/client.log: Global Timeout - Critical: sched-baseline/13-bloated_primary/server.log: Global Timeout - Critical: sched-baseline/14-bloated_secondary/client.log: Global Timeout - Critical: sched-baseline/14-bloated_secondary/server.log: Global Timeout - Critical: sched-baseline/15-slow_primary_upload/client.log: Global Timeout - Critical: sched-baseline/15-slow_primary_upload/server.log: Global Timeout - Critical: sched-baseline/16-balanced_upload/client.log: Global Timeout - Critical: sched-baseline/16-balanced_upload/server.log: Global Timeout - Critical: sched-baseline/17-slow_primary_parallel_4/client.log: Global Timeout - Critical: sched-baseline/17-slow_primary_parallel_4/server.log: Global Timeout - Critical: sched-baseline/18-balanced_parallel_4/client.log: Global Timeout - Critical: sched-baseline/18-balanced_parallel_4/server.log: Global Timeout - Critical: sched-bbr/01-slow_primary_bbr/client.log: Global Timeout - Critical: sched-bbr/01-slow_primary_bbr/server.log: Global Timeout - Critical: sched-bbr/02-balanced_bbr/client.log: Global Timeout - Critical: sched-bbr/02-balanced_bbr/server.log: Global Timeout - Critical: sched-bbr/03-slow_near_primary_bbr/client.log: Global Timeout - Critical: sched-bbr/03-slow_near_primary_bbr/server.log: Global Timeout - Critical: sched-bbr/04-high_lat_primary_bbr/client.log: Global Timeout - Critical: sched-bbr/04-high_lat_primary_bbr/server.log: Global Timeout - Critical: sched-bbr/05-huge_lat_primary_bbr/client.log: Global Timeout - Critical: sched-bbr/05-huge_lat_primary_bbr/server.log: Global Timeout - Critical: sched-bbr/06-high_lat_secondary_bbr/client.log: Global Timeout - Critical: sched-bbr/06-high_lat_secondary_bbr/server.log: Global Timeout - Critical: sched-bbr/07-huge_lat_secondary_bbr/client.log: Global Timeout - Critical: sched-bbr/07-huge_lat_secondary_bbr/server.log: Global Timeout - Critical: sched-bbr/08-slow_secondary_bbr/client.log: Global Timeout - Critical: sched-bbr/08-slow_secondary_bbr/server.log: Global Timeout - Critical: sched-bbr/09-very_fast_bbr/client.log: Global Timeout - Critical: sched-bbr/09-very_fast_bbr/server.log: Global Timeout - Critical: sched-bbr/10-bloated_secondary_bbr/client.log: Global Timeout - Critical: sched-bbr/10-bloated_secondary_bbr/server.log: Global Timeout - Critical: sched-bbr/11-bloated_slow_secondary_bbr/client.log: Global Timeout - Critical: sched-bbr/11-bloated_slow_secondary_bbr/server.log: Global Timeout - Critical: sched-bbr/12-very_bloated_secondary_bbr/client.log: Global Timeout - Critical: sched-bbr/12-very_bloated_secondary_bbr/server.log: Global Timeout - Critical: sched-bbr/13-bad_secondary_bbr/client.log: Global Timeout - Critical: sched-bbr/13-bad_secondary_bbr/server.log: Global Timeout - Critical: sched-bbr/14-far_secondary_bbr/client.log: Global Timeout - Critical: sched-bbr/14-far_secondary_bbr/server.log: Global Timeout - Critical: sched-clientlim/01-slow_primary_bw_5/client.log: Global Timeout - Critical: sched-clientlim/01-slow_primary_bw_5/server.log: Global Timeout - Critical: sched-clientlim/02-balanced_bw_5/client.log: Global Timeout - Critical: sched-clientlim/02-balanced_bw_5/server.log: Global Timeout - Critical: sched-clientlim/03-huge_lat_primary_bw_5/client.log: Global Timeout - Critical: sched-clientlim/03-huge_lat_primary_bw_5/server.log: Global Timeout - Critical: sched-clientlim/04-slow_secondary_bw_5/client.log: Global Timeout - Critical: sched-clientlim/04-slow_secondary_bw_5/server.log: Global Timeout - Critical: sched-clientlim/05-slow_primary_bw_10/client.log: Global Timeout - Critical: sched-clientlim/05-slow_primary_bw_10/server.log: Global Timeout - Critical: sched-clientlim/06-balanced_bw_10/client.log: Global Timeout - Critical: sched-clientlim/06-balanced_bw_10/server.log: Global Timeout - Critical: sched-clientlim/07-huge_lat_primary_bw_10/client.log: Global Timeout - Critical: sched-clientlim/07-huge_lat_primary_bw_10/server.log: Global Timeout - Critical: sched-clientlim/08-slow_secondary_bw_10/client.log: Global Timeout - Critical: sched-clientlim/08-slow_secondary_bw_10/server.log: Global Timeout - Critical: sched-clientlim/09-slow_primary_bw_50/client.log: Global Timeout - Critical: sched-clientlim/09-slow_primary_bw_50/server.log: Global Timeout - Critical: sched-clientlim/10-balanced_bw_50/client.log: Global Timeout - Critical: sched-clientlim/10-balanced_bw_50/server.log: Global Timeout - Critical: sched-clientlim/11-huge_lat_primary_bw_50/client.log: Global Timeout - Critical: sched-clientlim/11-huge_lat_primary_bw_50/server.log: Global Timeout - Critical: sched-clientlim/12-slow_secondary_bw_50/client.log: Global Timeout - Critical: sched-clientlim/12-slow_secondary_bw_50/server.log: Global Timeout - Critical: sched-cpu-preempt/01-shallow_primary/client.log: Global Timeout - Critical: sched-cpu-preempt/01-shallow_primary/server.log: Global Timeout - Critical: sched-cpu-preempt/02-shallow_primary_cpu_hog/client.log: Global Timeout - Critical: sched-cpu-preempt/02-shallow_primary_cpu_hog/server.log: Global Timeout - Critical: sched-mem-pressure/01-bloated_slow_secondary_receiver_tcp_mem_parallel_1/client.log: Global Timeout - Critical: sched-mem-pressure/01-bloated_slow_secondary_receiver_tcp_mem_parallel_1/server.log: Global Timeout - Critical: sched-mem-pressure/02-bloated_slow_secondary_receiver_tcp_mem_parallel_16/client.log: Global Timeout - Critical: sched-mem-pressure/02-bloated_slow_secondary_receiver_tcp_mem_parallel_16/server.log: Global Timeout - Critical: sched-mem-pressure/03-bloated_slow_secondary_receiver_tcp_mem_parallel_99/client.log: Global Timeout - Critical: sched-mem-pressure/03-bloated_slow_secondary_receiver_tcp_mem_parallel_99/server.log: Global Timeout - Critical: sched-mem-pressure/04-very_bloated_primary_both_tcp_mem_parallel_1/client.log: Global Timeout - Critical: sched-mem-pressure/04-very_bloated_primary_both_tcp_mem_parallel_1/server.log: Global Timeout - Critical: sched-mem-pressure/05-slow_primary_both_tcp_mem_parallel_1/client.log: Global Timeout - Critical: sched-mem-pressure/05-slow_primary_both_tcp_mem_parallel_1/server.log: Global Timeout - Critical: sched-mem-pressure/06-balanced_both_tcp_mem_parallel_1/client.log: Global Timeout - Critical: sched-mem-pressure/06-balanced_both_tcp_mem_parallel_1/server.log: Global Timeout - Critical: sched-mem-pressure/07-very_bloated_primary_both_tcp_mem_parallel_10/client.log: Global Timeout - Critical: sched-mem-pressure/07-very_bloated_primary_both_tcp_mem_parallel_10/server.log: Global Timeout - Critical: sched-mem-pressure/08-slow_primary_both_tcp_mem_parallel_10/client.log: Global Timeout - Critical: sched-mem-pressure/08-slow_primary_both_tcp_mem_parallel_10/server.log: Global Timeout - Critical: sched-mem-pressure/09-balanced_both_tcp_mem_parallel_10/client.log: Global Timeout - Critical: sched-mem-pressure/09-balanced_both_tcp_mem_parallel_10/server.log: Global Timeout - Critical: sched-rmem/01-slow_primary_rmem_200/client.log: Global Timeout - Critical: sched-rmem/01-slow_primary_rmem_200/server.log: Global Timeout - Critical: sched-rmem/02-balanced_rmem_200/client.log: Global Timeout - Critical: sched-rmem/02-balanced_rmem_200/server.log: Global Timeout - Critical: sched-rmem/03-huge_lat_primary_rmem_200/client.log: Global Timeout - Critical: sched-rmem/03-huge_lat_primary_rmem_200/server.log: Global Timeout - Critical: sched-rmem/04-bloated_primary_rmem_200/client.log: Global Timeout - Critical: sched-rmem/04-bloated_primary_rmem_200/server.log: Global Timeout - Critical: sched-rmem/05-slow_primary_rmem_800/client.log: Global Timeout - Critical: sched-rmem/05-slow_primary_rmem_800/server.log: Global Timeout - Critical: sched-rmem/06-balanced_rmem_800/client.log: Global Timeout - Critical: sched-rmem/06-balanced_rmem_800/server.log: Global Timeout - Critical: sched-rmem/07-huge_lat_primary_rmem_800/client.log: Global Timeout - Critical: sched-rmem/07-huge_lat_primary_rmem_800/server.log: Global Timeout - Critical: sched-rmem/08-bloated_primary_rmem_800/client.log: Global Timeout - Critical: sched-rmem/08-bloated_primary_rmem_800/server.log: Global Timeout - Critical: sched-rmem/09-slow_primary_rmem_2000/client.log: Global Timeout - Critical: sched-rmem/09-slow_primary_rmem_2000/server.log: Global Timeout - Critical: sched-rmem/10-balanced_rmem_2000/client.log: Global Timeout - Critical: sched-rmem/10-balanced_rmem_2000/server.log: Global Timeout - Critical: sched-rmem/11-huge_lat_primary_rmem_2000/client.log: Global Timeout - Critical: sched-rmem/11-huge_lat_primary_rmem_2000/server.log: Global Timeout - Critical: sched-rmem/12-bloated_primary_rmem_2000/client.log: Global Timeout - Critical: sched-rmem/12-bloated_primary_rmem_2000/server.log: Global Timeout - Critical: sched-rmem/13-slow_primary_rmem_6000/client.log: Global Timeout - Critical: sched-rmem/13-slow_primary_rmem_6000/server.log: Global Timeout - Critical: sched-rmem/14-balanced_rmem_6000/client.log: Global Timeout - Critical: sched-rmem/14-balanced_rmem_6000/server.log: Global Timeout - Critical: sched-rmem/15-huge_lat_primary_rmem_6000/client.log: Global Timeout - Critical: sched-rmem/15-huge_lat_primary_rmem_6000/server.log: Global Timeout - Critical: sched-rmem/16-bloated_primary_rmem_6000/client.log: Global Timeout - Critical: sched-rmem/16-bloated_primary_rmem_6000/server.log: Global Timeout - Critical: sched-serverlim/01-slow_primary_bw_5_burst_10/client.log: Global Timeout - Critical: sched-serverlim/01-slow_primary_bw_5_burst_10/server.log: Global Timeout - Critical: sched-serverlim/02-balanced_bw_5_burst_10/client.log: Global Timeout - Critical: sched-serverlim/02-balanced_bw_5_burst_10/server.log: Global Timeout - Critical: sched-serverlim/03-huge_lat_primary_bw_5_burst_10/client.log: Global Timeout - Critical: sched-serverlim/03-huge_lat_primary_bw_5_burst_10/server.log: Global Timeout - Critical: sched-serverlim/04-slow_secondary_bw_5_burst_10/client.log: Global Timeout - Critical: sched-serverlim/04-slow_secondary_bw_5_burst_10/server.log: Global Timeout - Critical: sched-serverlim/05-slow_primary_bw_5_burst_250/client.log: Global Timeout - Critical: sched-serverlim/05-slow_primary_bw_5_burst_250/server.log: Global Timeout - Critical: sched-serverlim/06-balanced_bw_5_burst_250/client.log: Global Timeout - Critical: sched-serverlim/06-balanced_bw_5_burst_250/server.log: Global Timeout - Critical: sched-serverlim/07-huge_lat_primary_bw_5_burst_250/client.log: Global Timeout - Critical: sched-serverlim/07-huge_lat_primary_bw_5_burst_250/server.log: Global Timeout - Critical: sched-serverlim/08-slow_secondary_bw_5_burst_250/client.log: Global Timeout - Critical: sched-serverlim/08-slow_secondary_bw_5_burst_250/server.log: Global Timeout - Critical: sched-serverlim/09-slow_primary_bw_10_burst_10/client.log: Global Timeout - Critical: sched-serverlim/09-slow_primary_bw_10_burst_10/server.log: Global Timeout - Critical: sched-serverlim/10-balanced_bw_10_burst_10/client.log: Global Timeout - Critical: sched-serverlim/10-balanced_bw_10_burst_10/server.log: Global Timeout - Critical: sched-serverlim/11-huge_lat_primary_bw_10_burst_10/client.log: Global Timeout - Critical: sched-serverlim/11-huge_lat_primary_bw_10_burst_10/server.log: Global Timeout - Critical: sched-serverlim/12-slow_secondary_bw_10_burst_10/client.log: Global Timeout - Critical: sched-serverlim/12-slow_secondary_bw_10_burst_10/server.log: Global Timeout - Critical: sched-serverlim/13-slow_primary_bw_10_burst_250/client.log: Global Timeout - Critical: sched-serverlim/13-slow_primary_bw_10_burst_250/server.log: Global Timeout - Critical: sched-serverlim/14-balanced_bw_10_burst_250/client.log: Global Timeout - Critical: sched-serverlim/14-balanced_bw_10_burst_250/server.log: Global Timeout - Critical: sched-serverlim/15-huge_lat_primary_bw_10_burst_250/client.log: Global Timeout - Critical: sched-serverlim/15-huge_lat_primary_bw_10_burst_250/server.log: Global Timeout - Critical: sched-serverlim/16-slow_secondary_bw_10_burst_250/client.log: Global Timeout - Critical: sched-serverlim/16-slow_secondary_bw_10_burst_250/server.log: Global Timeout - Critical: sched-serverlim/17-slow_primary_bw_50_burst_10/client.log: Global Timeout - Critical: sched-serverlim/17-slow_primary_bw_50_burst_10/server.log: Global Timeout - Critical: sched-serverlim/18-balanced_bw_50_burst_10/client.log: Global Timeout - Critical: sched-serverlim/18-balanced_bw_50_burst_10/server.log: Global Timeout - Critical: sched-serverlim/19-huge_lat_primary_bw_50_burst_10/client.log: Global Timeout - Critical: sched-serverlim/19-huge_lat_primary_bw_50_burst_10/server.log: Global Timeout - Critical: sched-serverlim/20-slow_secondary_bw_50_burst_10/client.log: Global Timeout - Critical: sched-serverlim/20-slow_secondary_bw_50_burst_10/server.log: Global Timeout - Critical: sched-serverlim/21-slow_primary_bw_50_burst_250/client.log: Global Timeout - Critical: sched-serverlim/21-slow_primary_bw_50_burst_250/server.log: Global Timeout - Critical: sched-serverlim/22-balanced_bw_50_burst_250/client.log: Global Timeout - Critical: sched-serverlim/22-balanced_bw_50_burst_250/server.log: Global Timeout - Critical: sched-serverlim/23-huge_lat_primary_bw_50_burst_250/client.log: Global Timeout - Critical: sched-serverlim/23-huge_lat_primary_bw_50_burst_250/server.log: Global Timeout - Critical: sched-serverlim/24-slow_secondary_bw_50_burst_250/client.log: Global Timeout - Critical: sched-serverlim/24-slow_secondary_bw_50_burst_250/server.log: Global Timeout - Critical: sched-serverlim/25-slow_primary_bw_10_burst_10_wmem_6000/client.log: Global Timeout - Critical: sched-serverlim/25-slow_primary_bw_10_burst_10_wmem_6000/server.log: Global Timeout - Critical: sched-serverlim/26-balanced_bw_10_burst_10_wmem_6000/client.log: Global Timeout - Critical: sched-serverlim/26-balanced_bw_10_burst_10_wmem_6000/server.log: Global Timeout - Critical: sched-serverlim/27-slow_primary_bw_50_burst_10_wmem_6000/client.log: Global Timeout - Critical: sched-serverlim/27-slow_primary_bw_50_burst_10_wmem_6000/server.log: Global Timeout - Critical: sched-serverlim/28-balanced_bw_50_burst_10_wmem_6000/client.log: Global Timeout - Critical: sched-serverlim/28-balanced_bw_50_burst_10_wmem_6000/server.log: Global Timeout - Critical: sched-weak-secondary/01-weak_secondary/client.log: Global Timeout - Critical: sched-weak-secondary/01-weak_secondary/server.log: Global Timeout - Critical: sched-weak-secondary/02-bad_secondary/client.log: Global Timeout - Critical: sched-weak-secondary/02-bad_secondary/server.log: Global Timeout - Critical: sched-weak-secondary/03-far_secondary/client.log: Global Timeout - Critical: sched-weak-secondary/03-far_secondary/server.log: Global Timeout - Critical: sched-weak-secondary/04-bloated_secondary/client.log: Global Timeout - Critical: sched-weak-secondary/04-bloated_secondary/server.log: Global Timeout - Critical: sched-weak-secondary/05-bloated_slow_secondary/client.log: Global Timeout - Critical: sched-weak-secondary/05-bloated_slow_secondary/server.log: Global Timeout - Critical: sched-weak-secondary/06-very_bloated_secondary/client.log: Global Timeout - Critical: sched-weak-secondary/06-very_bloated_secondary/server.log: Global Timeout - Critical: sched-weak-secondary/07-weak_secondary_backup/client.log: Global Timeout - Critical: sched-weak-secondary/07-weak_secondary_backup/server.log: Global Timeout - Critical: sched-weak-secondary/08-bad_secondary_backup/client.log: Global Timeout - Critical: sched-weak-secondary/08-bad_secondary_backup/server.log: Global Timeout - Critical: sched-weak-secondary/09-far_secondary_backup/client.log: Global Timeout - Critical: sched-weak-secondary/09-far_secondary_backup/server.log: Global Timeout - Critical: sched-weak-secondary/10-bloated_secondary_backup/client.log: Global Timeout - Critical: sched-weak-secondary/10-bloated_secondary_backup/server.log: Global Timeout - Critical: sched-weak-secondary/11-bloated_slow_secondary_backup/client.log: Global Timeout - Critical: sched-weak-secondary/11-bloated_slow_secondary_backup/server.log: Global Timeout - Critical: sched-weak-secondary/12-very_bloated_secondary_backup/client.log: Global Timeout - Critical: sched-weak-secondary/12-very_bloated_secondary_backup/server.log: Global Timeout ❌
- Task: https://github.com/multipath-tcp/mptcp_net-next/actions/runs/40833457021
Initiator: Patchew Applier
Commits: https://github.com/multipath-tcp/mptcp_net-next/commits/50ce0a995dda
Patchwork: https://patchwork.kernel.org/project/mptcp/list/?series=1195957
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] 8+ messages in thread