]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ipv6: fix possible infinite loop in fib6_info_uses_dev()
authorEric Dumazet <edumazet@google.com>
Fri, 25 Jul 2025 14:07:24 +0000 (14:07 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 15 Aug 2025 10:13:48 +0000 (12:13 +0200)
[ Upstream commit f8d8ce1b515a0a6af72b30502670a406cfb75073 ]

fib6_info_uses_dev() seems to rely on RCU without an explicit
protection.

Like the prior fix in rt6_nlmsg_size(),
we need to make sure fib6_del_route() or fib6_add_rt2node()
have not removed the anchor from the list, or we risk an infinite loop.

Fixes: d9ccb18f83ea ("ipv6: Fix soft lockups in fib6_select_path under high next hop churn")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20250725140725.3626540-4-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
net/ipv6/route.c

index f1e64c1cc49baa7ed4d858fa0ac628ef2f745720..8b84ed926cd116682a91c458225e0d6f9321c3df 100644 (file)
@@ -5875,16 +5875,21 @@ static bool fib6_info_uses_dev(const struct fib6_info *f6i,
        if (f6i->fib6_nh->fib_nh_dev == dev)
                return true;
 
-       if (f6i->fib6_nsiblings) {
-               struct fib6_info *sibling, *next_sibling;
+       if (READ_ONCE(f6i->fib6_nsiblings)) {
+               const struct fib6_info *sibling;
 
-               list_for_each_entry_safe(sibling, next_sibling,
-                                        &f6i->fib6_siblings, fib6_siblings) {
-                       if (sibling->fib6_nh->fib_nh_dev == dev)
+               rcu_read_lock();
+               list_for_each_entry_rcu(sibling, &f6i->fib6_siblings,
+                                       fib6_siblings) {
+                       if (sibling->fib6_nh->fib_nh_dev == dev) {
+                               rcu_read_unlock();
                                return true;
+                       }
+                       if (!READ_ONCE(f6i->fib6_nsiblings))
+                               break;
                }
+               rcu_read_unlock();
        }
-
        return false;
 }