MPTCP Linux Development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Geliang Tang" <geliang@kernel.org>, mptcp@lists.linux.dev
Subject: Re: [PATCH mptcp-next v19 5/5] mptcp: defer read_sock cleanup to mptcp_worker
Date: Fri, 30 Oct 2026 12:56:57 +0000	[thread overview]
Message-ID: <sashiko-outbox-191040@kernel.org> (raw)
In-Reply-To: <90a300d74f44443759ebe14be70c07385267db5b.1793361629.git.tanggeliang@kylinos.cn>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] mptcp: unsynchronized copied_seq when MPTCP_SYNC_SEQ is deferred
- [High] mptcp: wrong map_seq64 in mptcp_init_skb() on non-zero skb offset
--

commit 1033c608c403db5f17f6be77476b0bb66333000f
Author: Geliang Tang <geliang@kernel.org>

mptcp: defer read_sock cleanup to mptcp_worker

This series reworks MPTCP receive sequence tracking to use msk->copied_seq and
32-bit SKB control block sequence numbers aligned with TCP, and defers
__mptcp_read_sock() receive buffer autotuning and cleanup to mptcp_worker().

> diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
> index 02a4f479d9d8c..13078f2f3c498 100644
> --- a/net/mptcp/protocol.c
> +++ b/net/mptcp/protocol.c

[ ... ]

> @@ -424,21 +423,21 @@ 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;
>  
>  	/* 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);
> -	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_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;
>  
>  	__skb_unlink(skb, &ssk->sk_receive_queue);
>  
> @@ -446,15 +445,40 @@ static void mptcp_init_skb(struct sock *ssk, struct sk_buff *skb, int offset,
>  	skb_dst_drop(skb);
>  }

[ ... ]

>  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) {
> +	/* Be sure to sync the eventual fastopen dummy mapping before any other
> +	 * skb lands into the msk.
> +	 */
> +	if (unlikely(msk->rcvd_dummy_seq))
> +		__mptcp_sync_rcv_sequence(sk);
> +
> +	if (MPTCP_SKB_CB(skb)->map_seq64 == msk->ack_seq) {
>  		/* in sequence */
>  insert:
>  		if (!mptcp_try_rmem_schedule(sk, skb)) {
> @@ -472,18 +496,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;
> -		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;
> +	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]
As reported by Sashiko on v18 [1], can subtracting offset from
mptcp_subflow_get_mapped_dsn(subflow) in mptcp_init_skb() corrupt the receive
stream when an out-of-order subflow skb has a non-zero offset?

In __mptcp_move_skbs_from_subflow(), offset = seq - TCP_SKB_CB(skb)->seq is
non-zero when the leading offset bytes of skb on ssk->sk_receive_queue were
already consumed or discarded (for instance, when skb spans the end of a
duplicate DSS mapping discarded by mptcp_subflow_discard_data() in
subflow_check_data_avail() and the start of a new DSS mapping at
tp->copied_seq).

Setting MPTCP_SKB_CB(skb)->map_seq64 = mptcp_subflow_get_mapped_dsn(subflow) -
offset assigns DSNs [mapped_dsn - offset, mapped_dsn) to those already
discarded leading bytes 0..offset of skb.

When the new DSS mapping starting at offset is out-of-order at the MPTCP level
(mapped_dsn > msk->ack_seq because the intervening DSN range
[msk->ack_seq, mapped_dsn) is in flight on another subflow):

1. If mapped_dsn - offset <= msk->ack_seq < mapped_dsn, __mptcp_move_skb()
sees map_seq64 <= msk->ack_seq and map_seq64 + skb->len > msk->ack_seq. It
treats the out-of-order skb as in-sequence or a partial packet, queues it
onto sk->sk_receive_queue, and advances msk->ack_seq across the missing DSN
gap [msk->ack_seq, mapped_dsn), causing __mptcp_recvmsg_mskq() or
mptcp_recv_skb() to copy the stale leading bytes in 0..offset to user space.

2. If mapped_dsn - offset > msk->ack_seq, __mptcp_move_skb() queues skb into
msk->out_of_order_queue with map_seq = mapped_dsn - offset, so
mptcp_data_queue_ofo() and __mptcp_ofo_queue() treat bytes 0..offset as valid
data for [mapped_dsn - offset, mapped_dsn) and drop the real segments arriving
on other subflows as duplicate data.

[ ... ]

> @@ -2383,6 +2386,11 @@ static bool mptcp_move_skbs(struct sock *sk)
>  	bool enqueued = false;
>  	u32 moved = 0;
>  
> +	if (test_and_clear_bit(MPTCP_SYNC_SEQ, &msk->cb_flags)) {
> +		msk->copied_seq += msk->tfo_iasn;
> +		__mptcp_sync_rcv_sequence(sk);
> +	}
> +
>  	mptcp_data_lock(sk);
>  	while (mptcp_can_spool_backlog(sk, &skbs)) {
>  		mptcp_data_unlock(sk);
> @@ -2402,22 +2410,17 @@ static bool mptcp_move_skbs(struct sock *sk)
>  static unsigned int mptcp_inq_hint(const struct sock *sk)
>  {
>  	const struct mptcp_sock *msk = mptcp_sk(sk);
> -	const struct sk_buff *skb;
> +	u64 hint_val;
>  
> -	skb = skb_peek(&sk->sk_receive_queue);
> -	if (skb) {
> -		u64 hint_val = READ_ONCE(msk->ack_seq) - MPTCP_SKB_CB(skb)->map_seq;
> +	hint_val = READ_ONCE(msk->ack_seq) - READ_ONCE(msk->copied_seq);
> +	if (hint_val >= INT_MAX)
> +		return INT_MAX;
>  
> -		if (hint_val >= INT_MAX)
> -			return INT_MAX;
> -
> -		return (unsigned int)hint_val;
> -	}
> -
> -	if (sk->sk_state == TCP_CLOSE || (sk->sk_shutdown & RCV_SHUTDOWN))
> +	if (!hint_val &&
> +	    (sk->sk_state == TCP_CLOSE || (sk->sk_shutdown & RCV_SHUTDOWN)))
>  		return 1;
>  
> -	return 0;
> +	return (unsigned int)hint_val;
>  }
>  
>  static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,

[ ... ]

> @@ -2463,8 +2472,11 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
>  
>  		copied += bytes_read;
>  
> -		if (!list_empty(&msk->backlog_list) && mptcp_move_skbs(sk))
> +		if (!list_empty(&msk->backlog_list) && mptcp_move_skbs(sk)) {
> +			if (flags & MSG_PEEK)
> +				peek_seq = msk->copied_seq + copied;
>  			continue;
> +		}

[Severity: High]
As reported by Sashiko on v18 [2], can deferring the msk->copied_seq update
via MPTCP_SYNC_SEQ leave msk->copied_seq out of sync with msk->ack_seq and
sk->sk_receive_queue while the socket lock is held?

In subflow_set_remote_key() in net/mptcp/subflow.c, when the remote key and
IASN arrive in BH context while sock_owned_by_user(sk) is true, msk->ack_seq
is updated immediately to subflow->iasn while updating msk->copied_seq is
deferred:

	WRITE_ONCE(msk->ack_seq, subflow->iasn);
	WRITE_ONCE(msk->can_ack, true);
	atomic64_set(&msk->rcv_wnd_sent, subflow->iasn);
	msk->tfo_iasn = subflow->iasn;

	if (!sock_owned_by_user(sk)) {
		msk->copied_seq += subflow->iasn;
		__mptcp_sync_rcv_sequence(sk);
	} else {
		set_bit(MPTCP_SYNC_SEQ, &msk->cb_flags);
	}

This appears to leave several paths with an unsynchronized msk->copied_seq:

1. In mptcp_move_skbs(), test_and_clear_bit(MPTCP_SYNC_SEQ, &msk->cb_flags) is
checked only once before mptcp_data_lock(sk) rather than inside the
while (mptcp_can_spool_backlog(sk, &skbs)) loop. If the 3rd ACK and payload
arrive in BH after that initial check or while mptcp_data_lock(sk) is dropped
around __mptcp_move_skbs(), the next iteration of the loop spools the new skb
via __mptcp_move_skb(), which calls __mptcp_sync_rcv_sequence(sk) (updating
the TFO skb in sk->sk_receive_queue to the IASN sequence space and clearing
msk->rcvd_dummy_seq) and queues the new IASN-based skb while msk->copied_seq
remains unsynchronized for the next iteration of mptcp_recvmsg().

2. While MPTCP_SYNC_SEQ is pending, mptcp_inq_hint() subtracts the
pre-IASN msk->copied_seq from the IASN-based msk->ack_seq and returns INT_MAX.
In mptcp_recvmsg(), if the 3rd ACK or SYN-ACK arrives without payload while
lock_sock(sk) is held, msk->backlog_list is empty so mptcp_move_skbs() is not
called; both mptcp_rcv_space_adjust() and the TCP_CM_INQ cmsg calculation run
before release_sock(sk) and observe INT_MAX from mptcp_inq_hint(sk). Similarly,
in mptcp_ioctl() for SIOCINQ, if the remote key arrives in BH after
mptcp_move_skbs() checks MPTCP_SYNC_SEQ and before mptcp_inq_hint(sk), SIOCINQ
returns INT_MAX.

3. In mptcp_recvmsg(), when flags & MSG_PEEK is set and mptcp_move_skbs(sk)
consumes MPTCP_SYNC_SEQ (updating msk->copied_seq and the TFO skb map_seq) but
returns false because the backlog skb was out-of-order or dropped, peek_seq is
only updated inside the if (mptcp_move_skbs(sk)) branch and remains stale if
the loop continues without sleeping.

[1] https://lore.kernel.org/r/b6e4ed70642e2f86c041381cea810db82116b836.1793349369.git.geliang@kernel.org
[2] https://lore.kernel.org/r/b6e4ed70642e2f86c041381cea810db82116b836.1793349369.git.geliang@kernel.org

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1793361629.git.tanggeliang@kylinos.cn?part=5

  reply	other threads:[~2026-10-30 12:56 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-30 12:06 [PATCH mptcp-next v19 0/5] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
2026-10-30 12:06 ` [PATCH mptcp-next v19 1/5] mptcp: drop the mptcp_ooo_try_coalesce() helper Geliang Tang
2026-10-30 12:06 ` [PATCH mptcp-next v19 2/5] mptcp: drop the cant_coalesce CB field Geliang Tang
2026-10-30 12:29   ` sashiko-bot
2026-10-30 12:06 ` [PATCH mptcp-next v19 3/5] mptcp: remove CB offset field Geliang Tang
2026-10-30 12:52   ` sashiko-bot
2026-10-30 12:06 ` [PATCH mptcp-next v19 4/5] mptcp: sync mptcp skb cb layout with tcp one Geliang Tang
2026-10-30 12:39   ` sashiko-bot
2026-10-30 12:06 ` [PATCH mptcp-next v19 5/5] mptcp: defer read_sock cleanup to mptcp_worker Geliang Tang
2026-10-30 12:56   ` sashiko-bot [this message]
2026-10-30 14:06 ` [PATCH mptcp-next v19 0/5] Reduce the differences between TCP and MPTCP for TLS usage MPTCP CI

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-191040@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=geliang@kernel.org \
    --cc=mptcp@lists.linux.dev \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox