* [PATCH v4 net-next] gre: fix ERSPAN o_flags race/corruption in xmit and fill_info
@ 2026-08-12 14:22 Eric Dumazet
2026-08-14 13:42 ` Simon Horman
2026-08-14 20:10 ` patchwork-bot+netdevbpf
0 siblings, 2 replies; 3+ messages in thread
From: Eric Dumazet @ 2026-08-12 14:22 UTC (permalink / raw)
To: David S . Miller, Jakub Kicinski, Paolo Abeni
Cc: Simon Horman, Ido Schimmel, netdev, eric.dumazet, Eric Dumazet
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 shouldn't,
leading to GRE header corruption (injecting a key field into the ERSPAN
GRE header).
Fix this by:
1) Passing flags as an argument to __gre_xmit().
2) Using local stack flags in ipgre_xmit(), gre_tap_xmit(), and erspan_xmit()
to prevent TOCTOU data races with concurrent configuration updates,
and 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")
Signed-off-by: Eric Dumazet <edumazet@google.com>
---
v4: addressed sashiko's feedback.
v3: https://lore.kernel.org/netdev/20260811095237.2314234-1-edumazet@google.com/
v2: https://lore.kernel.org/netdev/20260720135132.3957146-1-edumazet@google.com/
net/ipv4/ip_gre.c | 42 +++++++++++++++++++++++-------------------
net/ipv6/ip6_gre.c | 5 ++---
2 files changed, 25 insertions(+), 22 deletions(-)
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 6a92607401e8aae5a10bd758820d37f51fd7a25b..82309efd417e0f1f6554e7028be8e05d769e932d 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -475,12 +475,9 @@ static int gre_rcv(struct sk_buff *skb)
static void __gre_xmit(struct sk_buff *skb, struct net_device *dev,
const struct iphdr *tnl_params,
- __be16 proto)
+ __be16 proto, const unsigned long *flags)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
- IP_TUNNEL_DECLARE_FLAGS(flags);
-
- ip_tunnel_flags_copy(flags, tunnel->parms.o_flags);
/* Push GRE header. */
gre_build_header(skb, tunnel->tun_hlen,
@@ -653,6 +650,7 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
+ IP_TUNNEL_DECLARE_FLAGS(flags);
const struct iphdr *tnl_params;
if (!pskb_inet_may_pull(skb))
@@ -688,11 +686,12 @@ static netdev_tx_t ipgre_xmit(struct sk_buff *skb,
tnl_params = &tunnel->parms.iph;
}
- if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT,
- tunnel->parms.o_flags)))
+ ip_tunnel_flags_copy(flags, tunnel->parms.o_flags);
+
+ if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT, flags)))
goto free_skb;
- __gre_xmit(skb, dev, tnl_params, skb->protocol);
+ __gre_xmit(skb, dev, tnl_params, skb->protocol, flags);
return NETDEV_TX_OK;
free_skb:
@@ -705,6 +704,7 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
+ IP_TUNNEL_DECLARE_FLAGS(flags);
bool truncate = false;
__be16 proto;
@@ -728,10 +728,12 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
truncate = true;
}
+ ip_tunnel_flags_copy(flags, tunnel->parms.o_flags);
+
/* Push ERSPAN header */
if (tunnel->erspan_ver == 0) {
proto = htons(ETH_P_ERSPAN);
- __clear_bit(IP_TUNNEL_SEQ_BIT, tunnel->parms.o_flags);
+ __clear_bit(IP_TUNNEL_SEQ_BIT, flags);
} else if (tunnel->erspan_ver == 1) {
erspan_build_header(skb, ntohl(tunnel->parms.o_key),
tunnel->index,
@@ -746,8 +748,8 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb,
goto free_skb;
}
- __clear_bit(IP_TUNNEL_KEY_BIT, tunnel->parms.o_flags);
- __gre_xmit(skb, dev, &tunnel->parms.iph, proto);
+ __clear_bit(IP_TUNNEL_KEY_BIT, flags);
+ __gre_xmit(skb, dev, &tunnel->parms.iph, proto, flags);
return NETDEV_TX_OK;
free_skb:
@@ -760,6 +762,7 @@ static netdev_tx_t gre_tap_xmit(struct sk_buff *skb,
struct net_device *dev)
{
struct ip_tunnel *tunnel = netdev_priv(dev);
+ IP_TUNNEL_DECLARE_FLAGS(flags);
if (!pskb_inet_may_pull(skb))
goto free_skb;
@@ -769,14 +772,15 @@ static netdev_tx_t gre_tap_xmit(struct sk_buff *skb,
return NETDEV_TX_OK;
}
- if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT,
- tunnel->parms.o_flags)))
+ ip_tunnel_flags_copy(flags, tunnel->parms.o_flags);
+
+ if (gre_handle_offloads(skb, test_bit(IP_TUNNEL_CSUM_BIT, flags)))
goto free_skb;
if (skb_cow_head(skb, dev->needed_headroom))
goto free_skb;
- __gre_xmit(skb, dev, &tunnel->parms.iph, htons(ETH_P_TEB));
+ __gre_xmit(skb, dev, &tunnel->parms.iph, htons(ETH_P_TEB), flags);
return NETDEV_TX_OK;
free_skb:
@@ -1560,12 +1564,15 @@ static size_t ipgre_get_size(const struct net_device *dev)
static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
- struct ip_tunnel *t = netdev_priv(dev);
- struct ip_tunnel_parm_kern *p = &t->parms;
+ const struct ip_tunnel *t = netdev_priv(dev);
+ const struct ip_tunnel_parm_kern *p = &t->parms;
IP_TUNNEL_DECLARE_FLAGS(o_flags);
ip_tunnel_flags_copy(o_flags, p->o_flags);
+ if (t->erspan_ver != 0 && !t->collect_md)
+ __set_bit(IP_TUNNEL_KEY_BIT, o_flags);
+
if (nla_put_u32(skb, IFLA_GRE_LINK, p->link) ||
nla_put_be16(skb, IFLA_GRE_IFLAGS,
gre_tnl_flags_to_gre_flags(p->i_flags)) ||
@@ -1608,12 +1615,9 @@ static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev)
static int erspan_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
- struct ip_tunnel *t = netdev_priv(dev);
+ const struct ip_tunnel *t = netdev_priv(dev);
if (t->erspan_ver <= 2) {
- if (t->erspan_ver != 0 && !t->collect_md)
- __set_bit(IP_TUNNEL_KEY_BIT, t->parms.o_flags);
-
if (nla_put_u8(skb, IFLA_GRE_ERSPAN_VER, t->erspan_ver))
goto nla_put_failure;
diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c
index b843116e9b703c701a1a28a85569501d83ede21d..0b3f386b51a2d8c97e67472bcccc15285f7fc15a 100644
--- a/net/ipv6/ip6_gre.c
+++ b/net/ipv6/ip6_gre.c
@@ -964,7 +964,6 @@ static netdev_tx_t ip6erspan_tunnel_xmit(struct sk_buff *skb,
if (skb_cow_head(skb, dev->needed_headroom ?: t->hlen))
goto tx_err;
- __clear_bit(IP_TUNNEL_KEY_BIT, t->parms.o_flags);
IPCB(skb)->flags = 0;
/* For collect_md mode, derive fl6 from the tunnel key,
@@ -2115,8 +2114,8 @@ static size_t ip6gre_get_size(const struct net_device *dev)
static int ip6gre_fill_info(struct sk_buff *skb, const struct net_device *dev)
{
- struct ip6_tnl *t = netdev_priv(dev);
- struct __ip6_tnl_parm *p = &t->parms;
+ const struct ip6_tnl *t = netdev_priv(dev);
+ const struct __ip6_tnl_parm *p = &t->parms;
IP_TUNNEL_DECLARE_FLAGS(o_flags);
ip_tunnel_flags_copy(o_flags, p->o_flags);
--
2.55.0.679.g6767b8d81c-goog
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH v4 net-next] gre: fix ERSPAN o_flags race/corruption in xmit and fill_info
2026-08-12 14:22 [PATCH v4 net-next] gre: fix ERSPAN o_flags race/corruption in xmit and fill_info Eric Dumazet
@ 2026-08-14 13:42 ` Simon Horman
2026-08-14 20:10 ` patchwork-bot+netdevbpf
1 sibling, 0 replies; 3+ messages in thread
From: Simon Horman @ 2026-08-14 13:42 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S . Miller, Jakub Kicinski, Paolo Abeni, Ido Schimmel,
netdev, eric.dumazet
On Wed, Aug 12, 2026 at 02:22:57PM +0000, Eric Dumazet 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 shouldn't,
> leading to GRE header corruption (injecting a key field into the ERSPAN
> GRE header).
>
> Fix this by:
> 1) Passing flags as an argument to __gre_xmit().
> 2) Using local stack flags in ipgre_xmit(), gre_tap_xmit(), and erspan_xmit()
> to prevent TOCTOU data races with concurrent configuration updates,
> and 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")
> Signed-off-by: Eric Dumazet <edumazet@google.com>
> ---
> v4: addressed sashiko's feedback.
> v3: https://lore.kernel.org/netdev/20260811095237.2314234-1-edumazet@google.com/
> v2: https://lore.kernel.org/netdev/20260720135132.3957146-1-edumazet@google.com/
Reviewed-by: Simon Horman <horms@kernel.org>
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH v4 net-next] gre: fix ERSPAN o_flags race/corruption in xmit and fill_info
2026-08-12 14:22 [PATCH v4 net-next] gre: fix ERSPAN o_flags race/corruption in xmit and fill_info Eric Dumazet
2026-08-14 13:42 ` Simon Horman
@ 2026-08-14 20:10 ` patchwork-bot+netdevbpf
1 sibling, 0 replies; 3+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-08-14 20:10 UTC (permalink / raw)
To: Eric Dumazet; +Cc: davem, kuba, pabeni, horms, idosch, netdev, eric.dumazet
Hello:
This patch was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Wed, 12 Aug 2026 14:22:57 +0000 you 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 shouldn't,
> leading to GRE header corruption (injecting a key field into the ERSPAN
> GRE header).
>
> [...]
Here is the summary with links:
- [v4,net-next] gre: fix ERSPAN o_flags race/corruption in xmit and fill_info
https://git.kernel.org/netdev/net-next/c/9958e69b9893
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-08-14 20:10 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-12 14:22 [PATCH v4 net-next] gre: fix ERSPAN o_flags race/corruption in xmit and fill_info Eric Dumazet
2026-08-14 13:42 ` Simon Horman
2026-08-14 20:10 ` patchwork-bot+netdevbpf
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.