Re: [PATCH mptcp-next 5/6] mptcp: trim the duplicated skb head at receive enqueue

Geliang Tang <[email protected]> Thu, 30 Jul 2026 09:15:11 +0800
Newsgroups dev.linux.lists.mptcp
Message-ID <[email protected]>
Hi Paolo,

On Wed, 2026-07-29 at 10:08 +0200, Paolo Abeni wrote:
> On 7/27/26 1:29 PM, Geliang Tang wrote:
> > The linear readers (recvmsg, read_sock, read_done) cope with that
> > by
> > computing a per-skb offset = copied_seq - map_seq and skipping it.
> > But
> > consumers that treat the receive queue as a single contiguous byte
> > stream cannot: the TLS strparser builds an anchor whose frag_list
> > is
> > the receive-queue skbs and reads it with a plain skb_copy_bits(),
> > which
> > has no per-skb offset knowledge. A record spanning such an skb
> > boundary
> > then reads the duplicated prefix and gets corrupted.
> 
> The above raises a question.
> 
> AFAICS, the critical skb layout is also possible with plain TCP -
> possibly is just less likely. How does TLS deal with that? I read the
> above as the TLS stream get corrupted, which sounds suspiciously too
> fragile to me?!? Or did I miss something?

My description was inaccurate. This only occurs in the MPTCP out-of-
order scenario. It does not happen with TCP.

> 
> This change adds a lot of complexity to the rx path, we want to avoid
> it.

I agree with you. This helper mptcp_trim_dup_head() does not need to be
called in __mptcp_move_skb(). It only needs to be called when
overlapping data occurs in __mptcp_ofo_queue(). This way, it won't
affect the efficiency of the rx path.

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;

    p = rb_first(&msk->out_of_order_queue);
    while (p) {
        ... ... 
        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 = ack_seq - MPTCP_SKB_CB(skb)->map_seq;
        
            /* trim overlapping prefix, if any */
            pr_debug("uncoalesced seq=%x ack seq=%x delta=%d\n",
                     MPTCP_SKB_CB(skb)->map_seq, ack_seq,
                     delta);
            if (mptcp_trim_head(skb, delta)) {
                    mptcp_drop(sk, skb);
                    continue;
            }
            MPTCP_SKB_CB(skb)->map_seq += delta;
            __skb_queue_tail(&sk->sk_receive_queue, skb);
        }
        msk->bytes_received += seq_delta;
        WRITE_ONCE(msk->ack_seq, msk->ack_seq + seq_delta);
        moved = true;
    }
    return moved;
}

This helper is actually a mirror of the tcp_trim_head(). If we could
export and reuse TCP's __pskb_trim_head(), this helper would become
much simpler:

static int mptcp_trim_head(struct sk_buff *skb, int delta)
{
        int eat; 

        if (skb_unclone_keeptruesize(skb, GFP_ATOMIC))
                return -ENOMEM;

        eat = min_t(int, delta, skb_headlen(skb));
        if (eat) {
                __skb_pull(skb, eat);
                delta -= eat; 
        }

        if (delta) {
                __pskb_trim_head(skb, delta);
                skb->len += skb_headlen(skb);
        }

        return 0;
}

Would this implementation be better? Please give me some feedback.

Thanks,
-Geliang

> 
> /P
>