mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 12:44:03 +02:00
gre: fix ERSPAN o_flags race/corruption in xmit and fill_info
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. Signed-off-by: Eric Dumazet <edumazet@google.com> Reviewed-by: Simon Horman <horms@kernel.org> Link: https://patch.msgid.link/20260812142257.21283-1-edumazet@google.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
486e5419b7
commit
9958e69b98
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user