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

[PATCH net 1/1] ip6_tunnel: avoid racing encap setup in changelink

From: Ren Wei <hidden>
Date: 2026-07-28 15:17:27
Subsystem: networking [general], networking [ipv4/ipv6], the rest · Maintainers: "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, David Ahern, Ido Schimmel, Linus Torvalds

From: Chai Zixuan <redacted>

ip6_tnl_changelink() can change encapsulation parameters while the
tunnel device is still accepting transmitters. A transmitter can
reserve headroom using the old encapsulation header length and then
build the packet after the live encapsulation state has changed. This
can cause skb_push() to underflow the skb head.

Validate new encapsulation parameters on a temporary tunnel object
first. During live updates on running tunnel devices, stop the TX
queues before waiting for existing transmitters with synchronize_net().
Then apply the new encapsulation state and tunnel parameters before
waking the queues again. This prevents both rejected changelink
requests from mutating the live tunnel and new transmitters from
entering after the synchronization point with mismatched state, without
leaving stopped TX queues on down devices.

Fixes: b3a27b519b22 ("ip6_tunnel: Add support for fou/gue encapsulation")
Cc: stable@vger.kernel.org
Reported-by: Vega <redacted>
Assisted-by: Codex:gpt-5.4
Signed-off-by: Chai Zixuan <redacted>
Signed-off-by: Ren Wei <redacted>
---
 net/ipv6/ip6_tunnel.c | 22 ++++++++++++++++++----
 1 file changed, 18 insertions(+), 4 deletions(-)
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index bf8e40af60b0..429f8151fe99 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -1562,15 +1562,26 @@ ip6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p)
 	ip6_tnl_link_config(t);
 }
 
-static void ip6_tnl_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p)
+static void ip6_tnl_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p,
+			   const struct ip6_tnl *encap_tnl)
 {
 	struct net *net = t->net;
 	struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+	bool running = netif_running(t->dev);
 
+	if (running)
+		netif_tx_disable(t->dev);
 	ip6_tnl_unlink(ip6n, t);
 	synchronize_net();
+	if (encap_tnl) {
+		t->encap = encap_tnl->encap;
+		t->encap_hlen = encap_tnl->encap_hlen;
+		t->hlen = t->encap_hlen + t->tun_hlen;
+	}
 	ip6_tnl_change(t, p);
 	ip6_tnl_link(ip6n, t);
+	if (running)
+		netif_tx_wake_all_queues(t->dev);
 	netdev_state_change(t->dev);
 }
 
@@ -1705,7 +1716,7 @@ ip6_tnl_siocdevprivate(struct net_device *dev, struct ifreq *ifr,
 			if (dev == ip6n->fb_tnl_dev)
 				ip6_tnl0_update(t, &p1, false);
 			else
-				ip6_tnl_update(t, &p1);
+				ip6_tnl_update(t, &p1, NULL);
 		}
 		if (!IS_ERR(t)) {
 			err = 0;
@@ -2105,9 +2116,11 @@ static int ip6_tnl_changelink(struct net_device *dev, struct nlattr *tb[],
 			      struct netlink_ext_ack *extack)
 {
 	struct ip6_tnl *t = netdev_priv(dev);
+	struct ip6_tnl nt = {};
 	struct __ip6_tnl_parm p;
 	struct net *net = t->net;
 	struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id);
+	struct ip6_tnl *encap_tnl = NULL;
 	struct ip_tunnel_encap ipencap;
 
 	if (!rtnl_dev_link_net_capable(dev, net))
@@ -2137,10 +2150,11 @@ static int ip6_tnl_changelink(struct net_device *dev, struct nlattr *tb[],
 	}
 
 	if (ip_tunnel_netlink_encap_parms(data, &ipencap)) {
-		int err = ip6_tnl_encap_setup(t, &ipencap);
+		int err = ip6_tnl_encap_setup(&nt, &ipencap);
 
 		if (err < 0)
 			return err;
+		encap_tnl = &nt;
 	}
 	ip6_tnl_netlink_parms(data, &p);
 	if (p.collect_md)
@@ -2153,7 +2167,7 @@ static int ip6_tnl_changelink(struct net_device *dev, struct nlattr *tb[],
 	} else
 		t = netdev_priv(dev);
 
-	ip6_tnl_update(t, &p);
+	ip6_tnl_update(t, &p, encap_tnl);
 	return 0;
 }
 
-- 
2.34.1
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help