syzbot reports that unregister_netdevice() can wait forever for a netdevsim device whose reference count never drops to zero. The leaked reference is held by an IPv6 local route created from addrconf. A late NETDEV_CHANGE notification can still reach addrconf_notify() after the device has entered NETREG_UNREGISTERING. The handler can then run automatic address configuration, add a link-local address and install its host route after unregister teardown has already started. The route nexthop takes a netdev reference in fib6_nh_init(), and there might not be a later ifdown pass to remove the newly created address and route. Do not run MTU, UP or CHANGE based IPv6 autoconfiguration once the device is unregistering. Keep NETDEV_DOWN and NETDEV_UNREGISTER handling unchanged so the teardown path can still remove existing IPv6 state. Reported-by: syzbot+e2af46126e0644cbebdd@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=e2af46126e0644cbebdd Signed-off-by: Xu Rao --- v2: - Drop READ_ONCE() around dev->reg_state. addrconf_notify() is called from the netdevice notifier path, so a plain load is sufficient. - Do not add a Fixes tag. The issue does not appear to be caused by a single commit, but by a long-standing unregister-time lifecycle gap. net/ipv6/addrconf.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 5476b6536eb7..a517e57cf86a 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3666,6 +3666,9 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, break; case NETDEV_CHANGEMTU: + if (dev->reg_state == NETREG_UNREGISTERING) + break; + /* if MTU under IPV6_MIN_MTU stop IPv6 on this interface. */ if (dev->mtu < IPV6_MIN_MTU) { addrconf_ifdown(dev, dev != net->loopback_dev); @@ -3691,6 +3694,9 @@ static int addrconf_notify(struct notifier_block *this, unsigned long event, fallthrough; case NETDEV_UP: case NETDEV_CHANGE: + if (dev->reg_state == NETREG_UNREGISTERING) + break; + if (idev && idev->cnf.disable_ipv6) break; -- 2.50.1