Thread (4 messages) 4 messages, 1 author, 23h ago

[PATCH v1 net 2/3] ipv6: Flush rt->from in rt6_uncached_list_flush_dev().

flat view
HOTtoday

From: Kuniyuki Iwashima <kuniyu@google.com>
Date: 2026-11-01 06:44:22
Subsystem: networking [general], networking [ipv4/ipv6], the rest · Maintainers: "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, David Ahern, Ido Schimmel, Linus Torvalds

When a device is unregistered, rt6_uncached_list_flush_dev()
iterates over uncached routes and switches the dying dev
with blackhole_netdev.

However, rt->from could be cached via sk->sk_rx_dst and
hold the device reference.

Then, unregister_netdevice() gets stuck with the splat
below. [0]

Let's release rt->from in rt6_uncached_list_flush_dev().

Note that ip6_rt_copy_init() could set rt->from even if
in6_dev_get() returns NULL (e.g. mtu < IPV6_MIN_MTU is
set concurrently).

Also, rt->from->nh could be replaced with another device
than rt_dev and rt_idev->dev, and in such a case,
nexthop_flush_dev() sets fib6_destroying before
rt6_uncached_list_flush_dev().

[0]:
unregister_netdevice: waiting for dummy2 to become free. Usage count = 2
ref_tracker: netdev@000000006fb9da8c has 1/1 users at
     netdev_get_by_index+0x14c/0x170
     fib6_nh_init+0x1ae/0x1950
     ip6_route_info_create_nh+0x15d/0x780
     ip6_route_add+0x55/0x150
     inet6_rtm_newroute+0x15d/0x1360
     rtnetlink_rcv_msg+0x606/0x710
     netlink_rcv_skb+0x14f/0x2f0
     netlink_unicast+0x71b/0x940
     netlink_sendmsg+0x595/0x840
     __sock_sendmsg+0x119/0x150
     ____sys_sendmsg+0x3e9/0x5c0
     ___sys_sendmsg+0x157/0x1a0
     __x64_sys_sendmsg+0x119/0x170
     do_syscall_64+0x109/0x330
     entry_SYSCALL_64_after_hwframe+0x77/0x7f

Fixes: 93531c674315 ("net/ipv6: separate handling of FIB entries from dst based routes")
Reported-by: Jason Zhang <redacted>
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
 net/ipv6/route.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index fae698c5522b..2eb884558c24 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -179,12 +179,20 @@ void rt6_uncached_list_flush_dev(struct net_device *dev)
 		list_for_each_entry_safe(rt, safe, &ul->head, dst.rt_uncached) {
 			struct inet6_dev *rt_idev = rt->rt6i_idev;
 			struct net_device *rt_dev = rt->dst.dev;
+			struct fib6_info *from;
+			bool release_fib;
+
+			rcu_read_lock();
+			from = rcu_dereference(rt->from);
+			release_fib = from && from->fib6_destroying;
+			rcu_read_unlock();
 
 			if (rt_idev &&
 			    (dev ? rt_idev->dev == dev :
 			     READ_ONCE(rt_idev->dev->reg_state) == NETREG_UNREGISTERED)) {
 				rt->rt6i_idev = in6_dev_get(blackhole_netdev);
 				in6_dev_put(rt_idev);
+				release_fib = true;
 			}
 
 			if (dev ? rt_dev == dev :
@@ -193,6 +201,12 @@ void rt6_uncached_list_flush_dev(struct net_device *dev)
 				netdev_ref_replace(rt_dev, blackhole_netdev,
 						   &rt->dst.dev_tracker,
 						   GFP_ATOMIC);
+				release_fib = true;
+			}
+
+			if (release_fib) {
+				from = unrcu_pointer(xchg(&rt->from, NULL));
+				fib6_info_release(from);
 			}
 
 			if (rt->dst.dev == blackhole_netdev &&
-- 
2.56.0.317.g454f8302e1-goog
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help