Thread (2 messages) 2 messages, 1 author, 2d ago

Re: [PATCH v2 net] gre: fix ERSPAN o_flags race/corruption in xmit and fill_info

From: Eric Dumazet <edumazet@google.com>
Date: 2026-07-21 15:37:23
Also in: stable
Subsystem: networking [general], networking [ipv4/ipv6], the rest · Maintainers: "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, David Ahern, Ido Schimmel, Linus Torvalds

On Mon, Jul 20, 2026 at 3:51 PM Eric Dumazet [off-list ref] wrote:
For IPv4 ERSPAN:
In erspan_xmit(), the driver clears IP_TUNNEL_SEQ_BIT (for version 0)
and IP_TUNNEL_KEY_BIT directly in the shared tunnel->parms.o_flags
structure. Since transmit paths can run locklessly and concurrently,
this leads to a data race.

Furthermore, modifying tunnel->parms.o_flags permanently alters the
tunnel configuration. To work around this, erspan_fill_info() (which
reports config to userspace) was setting IP_TUNNEL_KEY_BIT back. If
erspan_fill_info (running under RTNL) and erspan_xmit (running locklessly)
race, erspan_xmit might see IP_TUNNEL_KEY_BIT set when it shouldnt,
leading to GRE header corruption (injecting a key field into the ERSPAN
GRE header).

Fix this by:
1) Snapshotting tunnel->parms.o_flags onto the stack in ipgre_xmit(),
   gre_tap_xmit(), and __gre6_xmit(), and computing the GRE header length via
   gre_calc_hlen(flags) instead of reading tunnel->tun_hlen. This eliminates
   TOCTOU races between flags, offload checks, and header length.
   Note: Since tunnel->tun_hlen is no longer used in fast path xmit, it could be
   removed altogether in net-next.
2) Using local flags in erspan_xmit(), clearing IP_TUNNEL_CSUM_BIT and
   IP_TUNNEL_KEY_BIT (and IP_TUNNEL_SEQ_BIT for v0) before passing them to
   __gre_xmit().
3) Removing the racy modification of t->parms.o_flags in erspan_fill_info().
4) Forcing IP_TUNNEL_KEY_BIT in the reported flags for ERSPAN locally
   in ipgre_fill_info().

For IPv6 ERSPAN:
ip6erspan_tunnel_xmit() was locklessly clearing IP_TUNNEL_KEY_BIT in
t->parms.o_flags even though it does not use these flags for building
the GRE header (it uses local flags). This permanently corrupts the
configuration and races with ip6gre_fill_info() which reads it.

Remove the redundant and racy modification.
This should remove false sharing in a fast path.

Add const qualifiers in ipgre_fill_info(), erspan_fill_info()
and ip6gre_fill_info() to clarify that these methods are not
supposed to write any live parameters.

Fixes: 84e54fe0a5ea ("gre: introduce native tunnel support for ERSPAN")
Fixes: ee496694b9ee ("ip_gre: do not report erspan version on GRE interface")
Fixes: 5a963eb61b7c ("ip6_gre: Add ERSPAN native tunnel support")
Cc: stable@vger.kernel.org
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
v2: addressed Sashiko and Ido feedback.
v1: https://lore.kernel.org/netdev/20260615140333.3161072-1-edumazet@google.com/ (local)
Sashiko said:

<quote>
 Does this code generate malformed GRE headers for ERSPAN if the sequence bit
 is missing from the output flags?
 erspan_validate() permits configurations where the sequence bit is present in
 the input flags but missing from the output flags. In that case, the snapshot
 of the flags lacks IP_TUNNEL_SEQ_BIT.
 After clearing IP_TUNNEL_CSUM_BIT and IP_TUNNEL_KEY_BIT, if IP_TUNNEL_SEQ_BIT
 is also missing:
 net/ipv4/ip_gre.c:__gre_xmit() {
 ...
 gre_build_header(skb, gre_calc_hlen(flags),
 ...
 }
 gre_calc_hlen(flags) will evaluate to 4 bytes instead of the 8 bytes required
 by the ERSPAN protocol. Before this patch, the hardcoded tunnel->tun_hlen
 ensured an 8-byte header for ERSPAN.
 Could this result in structurally malformed packets being transmitted?
</quote>

What a mess.

pw-bot: cr

I will squash the following:
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 442a2c9513e84e84013b4a6d447dd5e243428c5c..e102c378eeb5f0e6e9e6f710db1e05826e0fb070
100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -739,11 +739,13 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
                                    tunnel->index,
                                    truncate, true);
                proto = htons(ETH_P_ERSPAN);
+               __set_bit(IP_TUNNEL_SEQ_BIT, flags);
        } else if (tunnel->erspan_ver == 2) {
                erspan_build_header_v2(skb, ntohl(tunnel->parms.o_key),
                                       tunnel->dir, tunnel->hwid,
                                       truncate, true);
                proto = htons(ETH_P_ERSPAN2);
+               __set_bit(IP_TUNNEL_SEQ_BIT, flags);
        } else {
                goto free_skb;
        }
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help