All of lore.kernel.org
 help / color / mirror / Atom feed
From: Steffen Klassert <steffen.klassert@secunet.com>
To: Guoshuai Yang <yangguoshuai@gmail.com>
Cc: Herbert Xu <herbert@gondor.apana.org.au>,
	Sabrina Dubroca <sd@queasysnail.net>,
	"David S . Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Simon Horman <horms@kernel.org>, David Ahern <dsahern@kernel.org>,
	<netdev@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
	<stable@vger.kernel.org>
Subject: Re: [PATCH net v4] xfrm: esp: reset outer TCP GSO metadata on espintcp input
Date: Fri, 6 Nov 2026 08:48:50 +0100	[thread overview]
Message-ID: <au2GYqYCqjfu45ku@gauss4> (raw)
In-Reply-To: <20261027225710.51832-1-yangguoshuai@gmail.com>

On Wed, Oct 28, 2026 at 06:57:10AM +0800, Guoshuai Yang wrote:
> When ESP packets are received over TCP (RFC 8229 espintcp), strparser
> slices individual length-prefixed ESP records from the outer TCP stream by
> cloning the incoming TCP skb(s) in __strp_recv().
> 
> If the outer TCP skb was GRO-coalesced before reaching strparser, its
> skb_shinfo carries the outer TCP stream's GSO metadata (gso_size, gso_segs,
> and gso_type). When an ESP record spans two outer TCP segments, strparser
> chains the spillover clone onto skb_shinfo(head)->frag_list.
> 
> Subsequently in esp_input() and esp6_input(), skb_cow_data() unshares the
> head skb via pskb_expand_head() (preserving the outer TCP gso_size and
> gso_type in the new private skb_shinfo) and calls skb_copy() on each cloned
> frag_list member, which copies the outer TCP GSO fields via
> skb_copy_header() onto a linear frag_list child skb allocated from slab
> with head_frag == 0.
> 
> Furthermore, on kernels with TCP fraglist GRO support, if an outer TCP GRO
> aggregate carrying SKB_GSO_FRAGLIST reaches the espintcp socket unsegmented
> (for example, when rx-gro-list is enabled on a device in an outer netns
> where tcp4/6_check_fraglist_gro() finds no local socket and the aggregate
> is forwarded across a NETIF_F_GSO_FRAGLIST-capable veth device into the
> netns hosting the espintcp socket) and is chained onto
> skb_shinfo(head)->frag_list by __strp_recv(), skb_cow_data() invokes
> skb_copy() / skb_copy_expand() on that frag_list member and hits
> WARN_ON_ONCE(skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST), failing with
> -ENOMEM before decryption even begins. (On older stable trees without TCP
> fraglist GRO, the pre-pass is a no-op.)
> 
> After ESP decryption and header removal, the resulting single inner packet
> still has skb_is_gso(skb) == true with the stale outer TCP gso_size, while
> skb_headlen(head_skb) is now smaller than that gso_size due to stripping
> the outer framing and ESP header. When this packet passes through
> gro_cells_receive() (which flushes frag_list packets via GRO_NORMAL
> without resetting gso_size) and is forwarded, validate_xmit_skb() invokes
> tcp_gso_segment() -> skb_segment(). Because head_skb is shorter than the
> stale gso_size and SKB_GSO_DODGY is not set, skb_segment() attempts to map
> the slab-backed frag_list member as a scatter-gather page fragment where
> list_skb->head_frag == 0. On current mainline kernels, skb_segment()
> triggers DEBUG_NET_WARN_ON_ONCE(1) and fails with -EINVAL, causing
> validate_xmit_skb() to drop all multi-segment espintcp packets. On stable
> kernels prior to the removal of BUG_ON(!list_skb->head_frag) in
> skb_segment() (such as 6.12.94 where this trace was captured), it crashes
> the kernel:
> 
>   Internal error: Oops - undefined instruction: 0 [#1] SMP ARM
>   PC is at skb_segment+0x574/0xd20
>   LR is at skb_segment+0x884/0xd20
>   Call trace:
>    skb_segment from tcp_gso_segment
>    tcp_gso_segment from inet_gso_segment
>    inet_gso_segment from skb_mac_gso_segment
>    skb_mac_gso_segment from __skb_gso_segment
>    __skb_gso_segment from validate_xmit_skb
>    validate_xmit_skb from __dev_queue_xmit
>    __dev_queue_xmit from ip_finish_output2
>    ip_finish_output2 from ip_output
>    ip_output from ip_sublist_rcv_finish
>    ip_sublist_rcv_finish from ip_sublist_rcv
>    ip_list_rcv from __netif_receive_skb_list_core
>    __netif_receive_skb_list_core from __netif_receive_skb_list
>    __netif_receive_skb_list from gro_normal_list
>    gro_normal_list from gro_cell_poll
> 
> Similarly, if a decrypted inner packet with a frag_list later takes the
> ip_do_fragment() frag_list fast path, each detached frag_list member is
> transmitted as a standalone IP fragment and would still carry the stale
> outer TCP GSO state when reaching validate_xmit_skb(). And even when an
> ESP record does not have a frag_list, leaving the stale outer TCP gso_size
> on the decrypted inner skb prevents dev_gro_receive() from initializing
> gso_size to the inner packet's skb_gro_len().
> 
> We cannot reset GSO state earlier in espintcp_rcv() / handle_esp() because
> the skb may still be a clone sharing skb_shinfo with the outer TCP skb held
> by strparser. Instead, in esp_input() and esp6_input():
> 1. Prior to skb_cow_data(), unshare/unclone and reset GSO metadata on any
>    frag_list member carrying SKB_GSO_FRAGLIST (unsharing any shared
>    predecessor frag_list members along the walk) so skb_copy() /
>    skb_copy_expand() inside skb_cow_data() does not hit WARN_ON_ONCE() and
>    drop the packet.
> 2. At skip_cow, where the head skb and all frag_list members are guaranteed
>    to be uncloned with private skb_shinfo, call skb_gso_reset() on both the
>    head skb and each skb_walk_frags() member.
> 
> Fixes: e27cca96cd68 ("xfrm: add espintcp (RFC 8229)")
> Cc: stable@vger.kernel.org
> Signed-off-by: Guoshuai Yang <yangguoshuai@gmail.com>

Applied to the ipsec tree, thanks a lot!

      reply	other threads:[~2026-11-06  7:48 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-27 22:57 [PATCH net v4] xfrm: esp: reset outer TCP GSO metadata on espintcp input Guoshuai Yang
2026-11-06  7:48 ` Steffen Klassert [this message]

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=au2GYqYCqjfu45ku@gauss4 \
    --to=steffen.klassert@secunet.com \
    --cc=davem@davemloft.net \
    --cc=dsahern@kernel.org \
    --cc=edumazet@google.com \
    --cc=herbert@gondor.apana.org.au \
    --cc=horms@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=sd@queasysnail.net \
    --cc=stable@vger.kernel.org \
    --cc=yangguoshuai@gmail.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.