A race condition exists between network device unregistration and
concurrent link state change events (e.g., usbnet_deferred_kevent).
During device unregistration, __dev_close_many() clears
__LINK_STATE_START and calls netdev_watchdog_down() to delete the
watchdog timer. However, a concurrent link state change can call
netif_carrier_on(), which checks netif_running(dev). If this check
happens before __LINK_STATE_START is cleared, it proceeds to call
netdev_watchdog_up(). If netdev_watchdog_up() is delayed and executes
after netdev_watchdog_down() has deleted the timer, it will re-arm the
timer. This leaves the timer armed during the final teardown phase,
eventually triggering the WARN_ON(timer_pending(&dev->watchdog_timer))
in dev_shutdown().
Attempting to fix this by adding a lock-free double-check in
netdev_watchdog_up() introduces a subtle refcount underflow bug.
Instead, the most robust approach is to ensure the timer is completely
dead during the final teardown phase. Since dev_shutdown() is called
from process context under the rtnl_lock(), it is safe to sleep and
block. We can use timer_delete_sync() in dev_shutdown() to deactivate
the timer and wait for any concurrently running callbacks to finish. If
the timer was left armed, timer_delete_sync() returns 1, allowing us to
safely call netdev_put() to balance the refcount. This guarantees that
the timer is definitively dead before the WARN_ON check.
Fixes: d7811e623dd4be3e3bdba2d6330f7f15541ee45f ("[NET]: Drop tx lock in dev_watchdog_up")
Assisted-by: Gemini:gemini-3.1-pro-preview Gemini:gemini-3-flash-preview
Reported-by:
syzbot+c9ecf6...@syzkaller.appspotmail.com
Link:
https://syzkaller.appspot.com/bug?extid=c9ecf60a8adb7629821e
Link:
https://syzkaller.appspot.com/ai_job?id=2f0da36f-92ca-4f82-a3f9-92648d57a2ae
To: <
da...@davemloft.net>
To: <
edum...@google.com>
To: <
j...@mojatatu.com>
To: <
ji...@resnulli.us>
To: <
ku...@kernel.org>
To: <
net...@vger.kernel.org>
To: <
pab...@redhat.com>
Cc: <
ho...@kernel.org>
Cc: <
linux-...@vger.kernel.org>
---
diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c
index a93321db8..e4e72077b 100644
--- a/net/sched/sch_generic.c
+++ b/net/sched/sch_generic.c
@@ -1493,6 +1493,8 @@ void dev_shutdown(struct net_device *dev)
qdisc_put(rtnl_dereference(dev->qdisc));
rcu_assign_pointer(dev->qdisc, &noop_qdisc);
+ if (timer_delete_sync(&dev->watchdog_timer))
+ netdev_put(dev, &dev->watchdog_dev_tracker);
WARN_ON(timer_pending(&dev->watchdog_timer));
}
base-commit: 5d6919055dec134de3c40167a490f33c74c12581
--
This is an AI-generated patch subject to moderation.
Reply with '#syz upstream' to send it to the mailing list.
Reply with '#syz reject' to reject it.
See for more information.