[PATCH net] seg6: fix tun_src RCU lifetime on namespace teardown
From: Tristan Madani
Date: Fri Oct 23 2026 - 15:53:05 EST
From: Tristan Madani <tristan@xxxxxxxxxxxxxxxxxxx>
seg6_net_exit() frees tun_src directly, but set_tun_src() in
seg6_iptunnel.c reads it under rcu_read_lock(). The pointer may
be freed while a concurrent reader is still accessing it.
Add a pre_exit callback to clear the tun_src pointer before the
implied synchronize_rcu_expedited(), so all RCU readers complete
before the actual kfree in the exit callback. Save the original
pointer in tun_src_old so exit can free it after the grace period,
since the RCU pointer is already NULL at that point. The init
error path frees tun_src directly since pre_exit has not run.
Fixes: 915d7e5e5930 ("ipv6: sr: add code base for control plane support of SR-IPv6")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Tristan Madani <tristan@xxxxxxxxxxxxxxxxxxx>
---
include/net/seg6.h | 1 +
net/ipv6/seg6.c | 12 +++++++++++-
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/include/net/seg6.h b/include/net/seg6.h
index 82b3fbbcbb935..210f4fa0d4dd7 100644
--- a/include/net/seg6.h
+++ b/include/net/seg6.h
@@ -36,6 +36,7 @@ static inline void update_csum_diff16(struct sk_buff *skb, __be32 *from,
struct seg6_pernet_data {
struct mutex lock;
struct in6_addr __rcu *tun_src;
+ struct in6_addr *tun_src_old;
#ifdef CONFIG_IPV6_SEG6_HMAC
struct rhashtable hmac_infos;
#endif
diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c
index 8c2b156c227a7..670052290e43c 100644
--- a/net/ipv6/seg6.c
+++ b/net/ipv6/seg6.c
@@ -447,18 +447,28 @@ static int __net_init seg6_net_init(struct net *net)
return 0;
}
+static void __net_exit seg6_net_pre_exit(struct net *net)
+{
+ struct seg6_pernet_data *sdata = seg6_pernet(net);
+
+ /* Save tun_src for exit to free after the RCU grace period. */
+ sdata->tun_src_old = rcu_dereference_raw(sdata->tun_src);
+ RCU_INIT_POINTER(sdata->tun_src, NULL);
+}
+
static void __net_exit seg6_net_exit(struct net *net)
{
struct seg6_pernet_data *sdata = seg6_pernet(net);
seg6_hmac_net_exit(net);
- kfree(rcu_dereference_raw(sdata->tun_src));
+ kfree(sdata->tun_src_old);
kfree(sdata);
}
static struct pernet_operations ip6_segments_ops = {
.init = seg6_net_init,
+ .pre_exit = seg6_net_pre_exit,
.exit = seg6_net_exit,
};
--
2.47.3