linkwatch_fire_event() queues a net_device onto lweventlist and takes a netdev_hold() even when the device has never been registered. netif_carrier_on(), netif_carrier_off() and netif_carrier_event() already skip NETREG_UNINITIALIZED. netif_dormant_*() and netif_testing_*() do not. Pre-registration operstate changes are meant to stay local and be consumed later by linkwatch_init_dev() in register_netdevice(). If a driver fires a dormant or testing event before register_netdevice() and registration then fails, rtnl_newlink_create() calls free_netdev(). For NETREG_UNINITIALIZED devices, free_netdev() immediately kvfree()s the object without draining linkwatch. The later linkwatch worker dereferences the stale list entry and use-after-frees the device. The same path is reachable from an unprivileged user who can unshare user and net namespaces, because only netns-local CAP_NET_ADMIN is required. linkwatch_fire_event() has never rejected NETREG_UNINITIALIZED devices. That goes back to commit 1da177e4c3f4 ("Linux-2.6.12-rc2"), the root of this tree. Later changes only altered callers or were reverted. commit b47300168e77 ("net: Do not fire linkwatch events until the device is registered.") skipped uninitialized devices in the carrier helpers only. commit 22604c866889 ("net: Fix for initial link state in 2.6.28") briefly added a NETREG_UNINITIALIZED return in the helper after calling rfc2863_policy(). commit c276e098d3ee ("Revert "net: Fix for initial link state in 2.6.28"") removed it again because that rfc2863_policy() call could take dev_base_lock in software-interrupt context. Fix this in linkwatch_fire_event() by ignoring NETREG_UNINITIALIZED devices so they cannot be queued, without calling rfc2863_policy(). Pre-registration flags remain on the device and are still applied by linkwatch_init_dev() if registration succeeds. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reported-by: Vega Assisted-by: LLM Signed-off-by: Zihan Xi --- net/core/link_watch.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/core/link_watch.c b/net/core/link_watch.c index 9c35aac8b2e98..1caa786df2c71 100644 --- a/net/core/link_watch.c +++ b/net/core/link_watch.c @@ -318,7 +318,12 @@ static void linkwatch_event(struct work_struct *dummy) void linkwatch_fire_event(struct net_device *dev) { - bool urgent = linkwatch_urgent_event(dev); + bool urgent; + + if (dev->reg_state == NETREG_UNINITIALIZED) + return; + + urgent = linkwatch_urgent_event(dev); if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) { linkwatch_add_event(dev); -- 2.43.0