From: sashiko-bot@kernel.org
To: "Geliang Tang" <geliang@kernel.org>, mptcp@lists.linux.dev
Subject: Re: [PATCH mptcp-next v17 3/4] mptcp: sync mptcp skb cb layout with tcp one
Date: Fri, 30 Oct 2026 02:49:41 +0000 [thread overview]
Message-ID: <sashiko-outbox-190183@kernel.org> (raw)
In-Reply-To: <fe11356d3e5637de94fbe89540271d5f4ee381b4.1793326853.git.tanggeliang@kylinos.cn>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Critical] mptcp: TFO skb UAF race and un-rebased copied_seq in subflow_set_remote_key()
- [Critical] mptcp: subtracting offset from map_seq64 in mptcp_init_skb() corrupts OFO data
- [High] mptcp: 64-bit vs 32-bit seq check in __mptcp_recvmsg_mskq() breaks MSG_PEEK
--
commit 62db3c701d55a6fd52b9c1e97a8f06064e99fb14
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 TCP by narrowing
MPTCP_SKB_CB(skb)->map_seq and end_seq to 32-bit fields while keeping the
full 64-bit sequence number in map_seq64. It updates the receive and
out-of-order queue paths to use 32-bit sequence comparisons after validating
incoming packets against the receive window.
> diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c
> index f3b1b6b282a2e..8447efa9dee6e 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 map_seq64 in mptcp_init_skb() corrupt stream data
when the subflow SKB has a non-zero offset and carries an out-of-order MPTCP
Data Sequence Mapping?
In __mptcp_move_skbs_from_subflow(), offset = tp->copied_seq -
TCP_SKB_CB(skb)->seq represents already-consumed or discarded subflow bytes at
the front of skb, while mptcp_subflow_get_mapped_dsn(subflow) is the DSN for
the byte starting at offset.
When the mapping is in sequence (mapped_dsn == msk->ack_seq ==
msk->copied_seq), subtracting offset from map_seq64 works because
__mptcp_recvmsg_mskq() later computes offset = (u32)msk->copied_seq -
MPTCP_SKB_CB(skb)->map_seq and skips the first offset bytes.
However, if offset > 0 and mapped_dsn > msk->ack_seq (out-of-order MPTCP
data), subtracting offset shifts map_seq64 and map_seq backwards into the
unreceived sequence range [mapped_dsn - offset, mapped_dsn):
1. In __mptcp_move_skb(), if mapped_dsn - offset <= msk->ack_seq < mapped_dsn,
the packet takes the in-sequence or partial-packet branch instead of
mptcp_data_queue_ofo(), advancing msk->ack_seq and exposing the stale
subflow prefix [0, offset) to user-space.
2. If mapped_dsn - offset > msk->ack_seq, mptcp_data_queue_ofo() inserts skb
into out_of_order_queue claiming coverage of [mapped_dsn - offset,
mapped_dsn + skb->len - offset), which causes legitimate segments for
[mapped_dsn - offset, mapped_dsn) to be dropped as duplicate data and
causes __mptcp_ofo_queue() to dequeue skb as soon as msk->ack_seq reaches
mapped_dsn - offset.
[ ... ]
> @@ -2192,7 +2199,7 @@ static int __mptcp_recvmsg_mskq(struct sock *sk, struct msghdr *msg,
> int err;
>
> if (msk->copied_seq >= MPTCP_SKB_CB(skb)->map_seq)
> - offset = msk->copied_seq - MPTCP_SKB_CB(skb)->map_seq;
> + offset = (u32)msk->copied_seq - MPTCP_SKB_CB(skb)->map_seq;
>
> /* Skip skbs that are entirely before copied_seq */
> if (offset >= skb->len) {
[Severity: High]
Does comparing the 64-bit msk->copied_seq against the 32-bit
MPTCP_SKB_CB(skb)->map_seq in __mptcp_recvmsg_mskq() break MSG_PEEK across
multiple SKBs and across 32-bit sequence wrap-around?
With struct mptcp_skb_cb narrowing map_seq to u32 while msk->copied_seq
remains u64, the condition:
if (msk->copied_seq >= MPTCP_SKB_CB(skb)->map_seq)
offset = (u32)msk->copied_seq - MPTCP_SKB_CB(skb)->map_seq;
zero-extends MPTCP_SKB_CB(skb)->map_seq to u64. Whenever the upper 32 bits of
msk->copied_seq are non-zero (which is typical since subflow->iasn is derived
from a 64-bit SHA-256 hash, or during TFO when msk->copied_seq is initialized
to -(u64)skb->len), msk->copied_seq >= MPTCP_SKB_CB(skb)->map_seq is
unconditionally true.
When recvmsg() is called with MSG_PEEK and sk->sk_receive_queue contains two
or more SKBs, msk->copied_seq is not updated after peeking the first SKB. On
the second SKB (where map_seq64 > msk->copied_seq), the condition still
evaluates to true, so offset = (u32)msk->copied_seq -
MPTCP_SKB_CB(skb)->map_seq underflows in 32-bit unsigned arithmetic to a huge
value (>= 2^31 > skb->len). That hits:
if (offset >= skb->len) {
*last = skb;
continue;
}
skipping all subsequent SKBs in sk->sk_receive_queue (and hanging in
sk_wait_data() when MSG_PEEK | MSG_WAITALL or SO_RCVLOWAT spans multiple
SKBs).
Conversely, if the upper 32 bits of msk->copied_seq are zero and an SKB
straddles a 32-bit wrap boundary (for example, map_seq = 0xfffffff0 and
copied_seq wraps to 0x10 after a partial read), 0x10ULL >= 0xfffffff0ULL
evaluates to false, leaving offset = 0 and re-reading already-consumed bytes.
Should this check compare against MPTCP_SKB_CB(skb)->map_seq64 (or use
!before((u32)msk->copied_seq, MPTCP_SKB_CB(skb)->map_seq))?
> diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
> index 6a66841ca24ae..5a8d837e55d04 100644
> --- a/net/mptcp/subflow.c
> +++ b/net/mptcp/subflow.c
> @@ -542,7 +542,8 @@ static void subflow_set_remote_key(struct mptcp_sock *msk,
> if (skb) {
> u64 tfo_map_seq = subflow->iasn - skb->len;
>
> - WRITE_ONCE(MPTCP_SKB_CB(skb)->map_seq, tfo_map_seq);
> + WRITE_ONCE(MPTCP_SKB_CB(skb)->map_seq, (u32)tfo_map_seq);
> + WRITE_ONCE(MPTCP_SKB_CB(skb)->map_seq64, tfo_map_seq);
> WRITE_ONCE(MPTCP_SKB_CB(skb)->end_seq, subflow->iasn);
>
> if (READ_ONCE(msk->copied_seq) != -(u64)skb->len)
[Severity: Critical]
What happens in subflow_set_remote_key() on a passive TFO socket if user-space
partially or concurrently reads the TFO SKB before the 3rd ACK arrives?
In mptcp_fastopen_subflow_synack_set_params(), the TFO SKB is queued onto
sk->sk_receive_queue with map_seq64 = -(u64)skb->len and msk->copied_seq =
-(u64)skb->len. There appear to be two issues when the 3rd ACK arrives in
check_fully_established() -> __mptcp_subflow_fully_established() ->
subflow_set_remote_key():
1. If user-space accepts the socket and partially reads N bytes (0 < N <
skb->len) before the 3rd ACK arrives, msk->copied_seq advances to
-(u64)skb->len + N while the SKB remains at the head of
sk->sk_receive_queue. When subflow_set_remote_key() runs:
if (READ_ONCE(msk->copied_seq) != -(u64)skb->len)
return;
it updates the SKB's map_seq, map_seq64, and end_seq to the subflow->iasn
sequence space, but returns early without rebasing msk->copied_seq by
+subflow->iasn (to tfo_map_seq + N). That leaves msk->copied_seq in the
initial 0-based sequence space while map_seq and msk->ack_seq are in the
subflow->iasn space, corrupting offset = (u32)msk->copied_seq -
MPTCP_SKB_CB(skb)->map_seq on subsequent reads.
2. In BH context, check_fully_established() only acquires
mptcp_data_lock(msk):
mptcp_data_lock((struct sock *)msk);
__mptcp_subflow_fully_established(msk, subflow, mp_opt);
mptcp_data_unlock((struct sock *)msk);
Meanwhile, a concurrent reader in __mptcp_recvmsg_mskq() or
mptcp_recv_skb() holds lock_sock(sk) (with sk_lock.slock released) and
calls mptcp_eat_recv_skb(), which unlinks and frees the SKB from
sk->sk_receive_queue without holding mptcp_data_lock(sk):
__skb_unlink(skb, &sk->sk_receive_queue);
skb_attempt_defer_free(skb);
Can subflow_set_remote_key() race with mptcp_eat_recv_skb() and trigger a
use-after-free when peeking sk->sk_receive_queue and writing to
MPTCP_SKB_CB(skb)?
--
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1793326853.git.tanggeliang@kylinos.cn?part=3
next prev parent reply other threads:[~2026-10-30 2:49 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-30 2:30 [PATCH mptcp-next v17 0/4] Reduce the differences between TCP and MPTCP for TLS usage Geliang Tang
2026-10-30 2:30 ` [PATCH mptcp-next v17 1/4] mptcp: drop the mptcp_ooo_try_coalesce() helper Geliang Tang
2026-10-30 2:30 ` [PATCH mptcp-next v17 2/4] mptcp: remove CB offset field Geliang Tang
2026-10-30 2:56 ` sashiko-bot
2026-10-30 2:30 ` [PATCH mptcp-next v17 3/4] mptcp: sync mptcp skb cb layout with tcp one Geliang Tang
2026-10-30 2:49 ` sashiko-bot [this message]
2026-10-30 2:30 ` [PATCH mptcp-next v17 4/4] mptcp: defer read_sock cleanup to mptcp_worker Geliang Tang
2026-10-30 4:24 ` [PATCH mptcp-next v17 0/4] 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-190183@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