From 19b4ed644d68098cc62ab612727f40d30f43476c Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Tue, 8 Sep 2026 07:42:56 +0000 Subject: [PATCH] ipv6: fix fib6 walker UAF on seq stop ipv6_route_iter_active() treats a walker in FWS_U at the table root as already unlinked. fib6_del_route() can move a still-linked walker into that same state when the current leaf is the last route at the root, so ipv6_route_native_seq_stop() skips fib6_walker_unlink(). The seq private object can then be freed while it remains on net->ipv6.fib6_walkers. A later route deletion walks the dangling list and uses the freed walker. Use the list head as membership state and reinitialize it when unlinking. Keep the existing w->node check so a never-started iterator with a zeroed private object is not treated as linked. The same stop helper is used by /proc/net/ipv6_route and by the BPF ipv6_route iterator. The BPF show path only widens the race. Fixes: 8d2ca1d7b5c3 ("ipv6: avoid high order memory allocations for /proc/net/ipv6_route") Cc: stable@vger.kernel.org Reported-by: Vega Co-developed-by: Luxing Yin Signed-off-by: Luxing Yin Signed-off-by: Zihan Xi Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/89699735763f6c297584d7c2ff106239cc1e8ce0.1788837093.git.zihanx@nebusec.ai Signed-off-by: Jakub Kicinski --- net/ipv6/ip6_fib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 3e382ba1573e..9ea75703b38d 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -85,7 +85,7 @@ static void fib6_walker_link(struct net *net, struct fib6_walker *w) static void fib6_walker_unlink(struct net *net, struct fib6_walker *w) { write_lock_bh(&net->ipv6.fib6_walker_lock); - list_del(&w->lh); + list_del_init(&w->lh); write_unlock_bh(&net->ipv6.fib6_walker_lock); } @@ -2760,7 +2760,7 @@ static void *ipv6_route_seq_start(struct seq_file *seq, loff_t *pos) static bool ipv6_route_iter_active(struct ipv6_route_iter *iter) { struct fib6_walker *w = &iter->w; - return w->node && !(w->state == FWS_U && w->node == w->root); + return w->node && !list_empty(&w->lh); } static void ipv6_route_native_seq_stop(struct seq_file *seq, void *v)