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]/