Re: [PATCH net v2] fou: ensure GUE headers have enough headroom

Ido Schimmel <[email protected]>
Newsgroups org.kernel.vger.netdev,org.kernel.vger.linux-kernel,org.kernel.vger.stable
Message-ID <20260824075011.GA858321@shredder>
On Fri, Aug 21, 2026 at 10:59:08PM +0800, Chengfeng Ye wrote:
> ipgre_changelink() installs GUE encapsulation before it publishes the
> new GRE header length and updates dev->needed_headroom.  The transmit
> path does not serialize with RTNL, so it can interleave as follows:
> 
>   CPU 0 (ipgre_changelink)        CPU 1 (ipgre_xmit)
>   install GUE encapsulation
>                                   reserve the old needed_headroom
>   publish larger GRE flags
>   update tunnel->tun_hlen
>                                   push the larger GRE header
>                                   push the GUE and UDP headers
>   update dev->needed_headroom
> 
> With REMCSUM, the new layout can push 16 bytes of GRE and 20 bytes of
> GUE/UDP headers into an skb with only 32 bytes of actual headroom.  The
> final UDP push writes four bytes before skb->head.
> 
> With the update window widened, the kernel reported:
> 
>   skbuff: skb_under_panic: ... len:128 put:8 ... dev:gre0poc
>   kernel BUG at net/core/skbuff.c:214!
>   Oops: invalid opcode: 0000 [#1] SMP KASAN NOPTI
>   Call Trace:
>    skb_push
>    fou_build_udp
>    gue_build_header
>    ip_tunnel_xmit
>    __gre_xmit
>    ipgre_xmit
> 
> Make __gue_build_header() ensure space for both the GUE header it is
> about to push and the UDP header that follows.  On normally sized skbs
> the check is a no-op.  If configuration changes race with transmission,
> skb_cow_head() expands the head before either GUE write, or returns an
> error without modifying the packet.
> 
> skb_cow_head() may move skb->head.  Refresh skb-derived inner_iph
> pointers after encapsulation, and read the tunnel fragment and TTL
> fields before ip_tunnel_encap() so ip_tunnel_xmit() does not dereference
> tnl_params after a possible skb head reallocation.

It is up to the caller of ip_tunnel_encap() to make sure that there is
enough headroom in the packet. Otherwise, what is the point of the
encap_hlen() callback? Also, adding skb_cow_head() in the build_header()
callback forces every caller to refresh pointers to skb->head.

There's a WIP patch to fix the same problem in IPv6 [1]. I suggest doing
something similar in ip_tunnel_xmit() and ip_md_tunnel_xmit(). Note that
IPv6 reserves the headroom before the encap push, so the snapshot is
enough there. IPv4 reserves it after, so we also need an skb_cow_head()
before ip_tunnel_encap().

[1] https://lore.kernel.org/netdev/[email protected]/
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.