xfrm: avoid stale tunnel pointers in deferred input callbacks
Subsystems (4)
PATCH 1/1 xfrm: avoid stale tunnel pointers in deferred input callbacks
IP-TFS input can be deferred while its reordering queue waits for a missing sequence number. The VTI receive callback stores a pointer to the tunnel in skb->cb, and the device can be removed and freed before the callback runs.
Keep only scalar tunnel metadata in an skb extension. The VTI callbacks look up the device by ifindex under RCU and check its link configuration before using its private data. If the VTI no longer exists, drop the deferred packet. Use the same metadata to preserve tunnel mark handling and IPv6 tunnel detection, and clear it when a new XFRM receive path starts and after the callback completes.
---
include/linux/skbuff.h +1 -0
@@ -5044,6 +5044,7 @@ enum skb_ext_id {#endif #ifdef CONFIG_XFRM SKB_EXT_SEC_PATH , +SKB_EXT_XFRM_TUNNEL , #endif #if IS_ENABLED(CONFIG_NET_TC_SKB_EXT) TC_SKB_EXT,
include/net/xfrm.h +48 -7
90 lines not on this page · raw message
net/core/skbuff.c +1 -0
@@ -5241,6 +5241,7 @@ static const u8 skb_ext_type_len[] = {#endif #ifdef CONFIG_XFRM [SKB_EXT_SEC_PATH] = SKB_EXT_CHUNKSIZEOF(struct sec_path), + [SKB_EXT_XFRM_TUNNEL] = SKB_EXT_CHUNKSIZEOF(struct xfrm_tunnel_info), #endif #if IS_ENABLED(CONFIG_NET_TC_SKB_EXT) [TC_SKB_EXT] = SKB_EXT_CHUNKSIZEOF(struct tc_skb_ext),
net/ipv4/esp4_offload.c +1 -1
@@ -81,7 +81,7 @@ static struct sk_buff *esp4_gro_receive(struct list_head *head,if (NAPI_GRO_CB (skb)->proto ==IPPROTO_UDP ) encap_type =UDP_ENCAP_ESPINUDP ; -XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip4 =NULL ; +xfrm_tunnel_info_clear (skb);XFRM_SPI_SKB_CB (skb)->family =AF_INET ;XFRM_SPI_SKB_CB (skb)->daddroff = offsetof(struct iphdr , daddr);XFRM_SPI_SKB_CB (skb)->seq = seq;
net/ipv4/ip_vti.c +30 -5
@@ -58,10 +58,25 @@ static int vti_input(struct sk_buff *skb, int nexthdr, __be32 spi, tunnel =ip_tunnel_lookup (itn, skb->dev->ifindex, flags, iph->saddr, iph->daddr,0 );if (tunnel) { +struct xfrm_tunnel_info *info; +if (!xfrm4_policy_check (NULL ,XFRM_POLICY_IN , skb)) goto drop; -XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip4 = tunnel; + info =xfrm_tunnel_info_add (skb); +if (!info) + goto drop; + + *info = (struct xfrm_tunnel_info ) { + .ifindex = tunnel->dev->ifindex, + .i_key = tunnel->parms.i_key, + .type =XFRM_TUNNEL_INFO_VTI4 , + .ip4 = { + .link = tunnel->parms.link, + .saddr = tunnel->parms.iph.saddr, + .daddr = tunnel->parms.iph.daddr, + }, + };if (update_skb_dev) skb->dev = tunnel->dev;
@@ -96,18 +111,28 @@ static int vti_rcv_proto(struct sk_buff *skb)static int vti_rcv_cb (struct sk_buff *skb,int err) { +const struct xfrm_tunnel_info *info =xfrm_tunnel_info (skb);unsigned short family;struct net_device *dev;struct xfrm_state *x;const struct xfrm_mode *inner_mode; -struct ip_tunnel *tunnel =XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip4; +struct ip_tunnel *tunnel;u32 orig_mark = skb->mark;int ret; -if (!tunnel) +if (!info || info->type !=XFRM_TUNNEL_INFO_VTI4 )return 1 ; - dev = tunnel->dev; + dev =dev_get_by_index_rcu (dev_net (skb->dev), info->ifindex); +if (!dev || dev->rtnl_link_ops != &vti_link_ops) +return err ?1 : -ENODEV ; + + tunnel =netdev_priv (dev); +if (tunnel->parms.i_key != info->i_key || + tunnel->parms.link != info->ip4.link || + tunnel->parms.iph.saddr != info->ip4.saddr || + tunnel->parms.iph.daddr != info->ip4.daddr) +return err ?1 : -ENODEV ;if (err) {DEV_STATS_INC (dev, rx_errors);
@@ -131,7 +156,7 @@ static int vti_rcv_cb(struct sk_buff *skb, int err) family = inner_mode->family; - skb->mark =be32_to_cpu (tunnel->parms.i_key); + skb->mark =be32_to_cpu (info->i_key); ret =xfrm_policy_check (NULL ,XFRM_POLICY_IN , skb, family); skb->mark = orig_mark;
net/ipv4/xfrm4_protocol.c +4 -4
@@ -65,7 +65,7 @@ int xfrm4_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,struct xfrm4_protocol *handler;struct xfrm4_protocol __rcu **head =proto_handlers (nexthdr); -XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip4 =NULL ; +xfrm_tunnel_info_clear (skb);XFRM_SPI_SKB_CB (skb)->family =AF_INET ;XFRM_SPI_SKB_CB (skb)->daddroff = offsetof(struct iphdr , daddr);
@@ -98,7 +98,7 @@ static int xfrm4_esp_rcv(struct sk_buff *skb)int ret;struct xfrm4_protocol *handler; -XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip4 =NULL ; +xfrm_tunnel_info_clear (skb);for_each_protocol_rcu (esp4_handlers, handler)if ((ret = handler->handler (skb)) != -EINVAL )
@@ -126,7 +126,7 @@ static int xfrm4_ah_rcv(struct sk_buff *skb)int ret;struct xfrm4_protocol *handler; -XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip4 =NULL ; +xfrm_tunnel_info_clear (skb);for_each_protocol_rcu (ah4_handlers, handler)if ((ret = handler->handler (skb)) != -EINVAL )
@@ -154,7 +154,7 @@ static int xfrm4_ipcomp_rcv(struct sk_buff *skb)int ret;struct xfrm4_protocol *handler; -XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip4 =NULL ; +xfrm_tunnel_info_clear (skb);for_each_protocol_rcu (ipcomp4_handlers, handler)if ((ret = handler->handler (skb)) != -EINVAL )
net/ipv6/esp6_offload.c +1 -1
@@ -110,7 +110,7 @@ static struct sk_buff *esp6_gro_receive(struct list_head *head, goto out;IP6CB (skb)->nhoff = nhoff; -XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip6 =NULL ; +xfrm_tunnel_info_clear (skb);XFRM_SPI_SKB_CB (skb)->family =AF_INET6 ;XFRM_SPI_SKB_CB (skb)->daddroff = offsetof(struct ipv6hdr , daddr);XFRM_SPI_SKB_CB (skb)->seq = seq;
net/ipv6/ip6_vti.c +35 -5
@@ -302,6 +302,8 @@ static int vti6_input_proto(struct sk_buff *skb, int nexthdr, __be32 spi,rcu_read_lock (); t =vti6_tnl_lookup (dev_net (skb->dev), &ipv6h->saddr, &ipv6h->daddr);if (t) { +struct xfrm_tunnel_info *info; +if (t->parms.proto !=IPPROTO_IPV6 && t->parms.proto !=0 ) {rcu_read_unlock (); goto discard;
@@ -319,9 +321,26 @@ static int vti6_input_proto(struct sk_buff *skb, int nexthdr, __be32 spi, goto discard; } + info =xfrm_tunnel_info_add (skb); +if (!info) { +rcu_read_unlock (); + goto discard; + } + + *info = (struct xfrm_tunnel_info ) { + .ifindex = t->dev->ifindex, + .i_key = t->parms.i_key, + .type =XFRM_TUNNEL_INFO_VTI6 , + .ip6 = { + .link = t->parms.link, + .proto = t->parms.proto, + .laddr = t->parms.laddr, + .raddr = t->parms.raddr, + }, + }; +rcu_read_unlock (); -XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip6 = t;XFRM_SPI_SKB_CB (skb)->family =AF_INET6 ;XFRM_SPI_SKB_CB (skb)->daddroff = offsetof(struct ipv6hdr , daddr);return xfrm_input (skb, nexthdr, spi, encap_type);
@@ -342,18 +361,29 @@ static int vti6_rcv(struct sk_buff *skb)static int vti6_rcv_cb (struct sk_buff *skb,int err) { +const struct xfrm_tunnel_info *info =xfrm_tunnel_info (skb);unsigned short family;struct net_device *dev;struct xfrm_state *x;const struct xfrm_mode *inner_mode; -struct ip6_tnl *t =XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip6; +struct ip6_tnl *t;u32 orig_mark = skb->mark;int ret; -if (!t) +if (!info || info->type !=XFRM_TUNNEL_INFO_VTI6 )return 1 ; - dev = t->dev; + dev =dev_get_by_index_rcu (dev_net (skb->dev), info->ifindex); +if (!dev || dev->rtnl_link_ops != &vti6_link_ops) +return err ?1 : -ENODEV ; + + t =netdev_priv (dev); +if (t->parms.i_key != info->i_key || + t->parms.link != info->ip6.link || + t->parms.proto != info->ip6.proto || + !ipv6_addr_equal (&t->parms.laddr, &info->ip6.laddr) || + !ipv6_addr_equal (&t->parms.raddr, &info->ip6.raddr)) +return err ?1 : -ENODEV ;if (err) {DEV_STATS_INC (dev, rx_errors);
@@ -377,7 +407,7 @@ static int vti6_rcv_cb(struct sk_buff *skb, int err) family = inner_mode->family; - skb->mark =be32_to_cpu (t->parms.i_key); + skb->mark =be32_to_cpu (info->i_key); ret =xfrm_policy_check (NULL ,XFRM_POLICY_IN , skb, family); skb->mark = orig_mark;
net/ipv6/xfrm6_input.c +23 -1
@@ -15,6 +15,7 @@#include <linux/netfilter.h> #include <linux/netfilter_ipv6.h> #include <net/ipv6.h> +#include <net/ip6_tunnel.h> #include <net/xfrm.h> #include <net/protocol.h> #include <net/gro.h>
@@ -22,7 +23,28 @@int xfrm6_rcv_spi (struct sk_buff *skb,int nexthdr,__be32 spi,struct ip6_tnl *t) { -XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip6 = t; +struct xfrm_tunnel_info *info; + +xfrm_tunnel_info_clear (skb); +if (t) { + info =xfrm_tunnel_info_add (skb); +if (!info) { +kfree_skb (skb); +return 0 ; + } + + *info = (struct xfrm_tunnel_info ) { + .ifindex = t->dev->ifindex, + .i_key = t->parms.i_key, + .type =XFRM_TUNNEL_INFO_IP6 , + .ip6 = { + .link = t->parms.link, + .proto = t->parms.proto, + .laddr = t->parms.laddr, + .raddr = t->parms.raddr, + }, + }; + }XFRM_SPI_SKB_CB (skb)->family =AF_INET6 ;XFRM_SPI_SKB_CB (skb)->daddroff = offsetof(struct ipv6hdr , daddr);return xfrm_input (skb, nexthdr, spi,0 );
net/ipv6/xfrm6_protocol.c +4 -4
@@ -66,7 +66,7 @@ int xfrm6_rcv_encap(struct sk_buff *skb, int nexthdr, __be32 spi,struct xfrm6_protocol *handler;struct xfrm6_protocol __rcu **head =proto_handlers (nexthdr); -XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip6 =NULL ; +xfrm_tunnel_info_clear (skb);XFRM_SPI_SKB_CB (skb)->family =AF_INET6 ;XFRM_SPI_SKB_CB (skb)->daddroff = offsetof(struct ipv6hdr , daddr);
@@ -113,7 +113,7 @@ static int xfrm6_esp_rcv(struct sk_buff *skb)int ret;struct xfrm6_protocol *handler; -XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip6 =NULL ; +xfrm_tunnel_info_clear (skb);for_each_protocol_rcu (esp6_handlers, handler)if ((ret = handler->handler (skb)) != -EINVAL )
@@ -142,7 +142,7 @@ static int xfrm6_ah_rcv(struct sk_buff *skb)int ret;struct xfrm6_protocol *handler; -XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip6 =NULL ; +xfrm_tunnel_info_clear (skb);for_each_protocol_rcu (ah6_handlers, handler)if ((ret = handler->handler (skb)) != -EINVAL )
@@ -171,7 +171,7 @@ static int xfrm6_ipcomp_rcv(struct sk_buff *skb)int ret;struct xfrm6_protocol *handler; -XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip6 =NULL ; +xfrm_tunnel_info_clear (skb);for_each_protocol_rcu (ipcomp6_handlers, handler)if ((ret = handler->handler (skb)) != -EINVAL )
net/xfrm/xfrm_input.c +10 -4
@@ -466,6 +466,7 @@ static int xfrm_inner_mode_input(struct xfrm_state *x,int xfrm_input (struct sk_buff *skb,int nexthdr,__be32 spi,int encap_type) {const struct xfrm_state_afinfo *afinfo; +const struct xfrm_tunnel_info *tunnel_info;struct net *net =dev_net (skb->dev);struct net_device *dev = skb->dev;int err;
@@ -555,16 +556,20 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) } family =XFRM_SPI_SKB_CB (skb)->family; + tunnel_info =xfrm_tunnel_info (skb);/* if tunnel is present override skb->mark value with tunnel i_key */ switch (family) {case AF_INET : -if (XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip4) - mark =be32_to_cpu (XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip4->parms.i_key); +if (tunnel_info && + tunnel_info->type ==XFRM_TUNNEL_INFO_VTI4 ) + mark =be32_to_cpu (tunnel_info->i_key);break ;case AF_INET6 : -if (XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip6) - mark =be32_to_cpu (XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip6->parms.i_key); +if (tunnel_info && + (tunnel_info->type ==XFRM_TUNNEL_INFO_VTI6 || + tunnel_info->type ==XFRM_TUNNEL_INFO_IP6 )) + mark =be32_to_cpu (tunnel_info->i_key);break ; }
@@ -739,6 +744,7 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) goto drop; } +xfrm_tunnel_info_clear (skb);nf_reset_ct (skb);if (decaps) {
net/xfrm/xfrm_interface_core.c +2 -2
@@ -322,10 +322,10 @@ static int xfrmi_input(struct sk_buff *skb, int nexthdr, __be32 spi,XFRM_SPI_SKB_CB (skb)->family = family;if (family ==AF_INET ) {XFRM_SPI_SKB_CB (skb)->daddroff = offsetof(struct iphdr , daddr); -XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip4 =NULL ; +xfrm_tunnel_info_clear (skb); }else {XFRM_SPI_SKB_CB (skb)->daddroff = offsetof(struct ipv6hdr , daddr); -XFRM_TUNNEL_SKB_CB (skb)->tunnel.ip6 =NULL ; +xfrm_tunnel_info_clear (skb); }return xfrm_input (skb, nexthdr, spi, encap_type);
[email protected]
AI reviewThis is an automated message. This series looks like a fix, but its commit messages seem to be missing some information:
Hi!
This is an automated message. This series looks like a fix, but its commit messages seem to be missing some information:
- How the issue was discovered, e.g. hit in production, hit during development, syzbot report, manual code inspection, LLM or static analysis tool scan.
- Whether the issue was actually triggered, or is only theoretical (e.g. found by LLM and not proven at runtime). If it was triggered please include the symptoms, like the stack trace or kernel messages.
Please do not repost the series just to address the above. Instead, reply to this email with the missing information, so that reviewers can take it into account. If the series needs another revision for other reasons, please include the information in the commit messages then.
The evaluation is done by an LLM so it may be wrong, if you think that is the case please reply and explain.