[PATCH] wifi: nl80211: serialize socket owner release with netdev teardown

From: Cen Zhang

Date: Fri Jun 19 2026 - 12:26:55 EST


nl80211_netlink_notify() walks the cfg80211 wireless device list when a
NETLINK_GENERIC socket is released. If the socket owns a connection, the
notifier queues the embedded wdev->disconnect_wk work item.

NETDEV_GOING_DOWN tears the interface down and cancels disconnect_wk, and
NETDEV_UNREGISTER removes the wireless device from the cfg80211 list. There
is no ordering between those paths today, so the netlink notifier can pass
the conn_owner_nlportid check before teardown clears it, then queue
disconnect_wk after the NETDEV_GOING_DOWN cancel has already returned.
synchronize_net() only waits for the notifier to finish; it does not drain
the work that the notifier just queued.

The buggy scenario involves two paths, with each column showing the order
within that path:

NETLINK_URELEASE path: netdev teardown path:
1. enter nl80211_netlink_notify() 1. run NETDEV_GOING_DOWN
2. match conn_owner_nlportid 2. clear connection owner state
3. get preempted before scheduling 3. cancel disconnect_wk
4. schedule disconnect_wk 4. unregister and free the netdev

Take RTNL while the notifier walks cfg80211 devices and schedules owner
cleanup work. Netdevice notifier delivery already runs under RTNL, so the
schedule and cancel are ordered: either the work is queued before
NETDEV_GOING_DOWN and the existing cancel drains it, or teardown runs first
and the notifier no longer queues work for that connection owner.

Validation reproduced this kernel report:
BUG: KASAN: use-after-free in cfg80211_autodisconnect_wk+0x338/0x3a0
Workqueue: events cfg80211_autodisconnect_wk [cfg80211]
Read of size 8
Call trace:
dump_stack_lvl+0x66/0xa0
print_report+0xce/0x630
cfg80211_autodisconnect_wk+0x338/0x3a0
srso_alias_return_thunk+0x5/0xfbef5
__virt_addr_valid+0x224/0x430
kasan_report+0xac/0xe0
process_one_work+0x8d0/0x18f0 (kernel/workqueue.c:3212)
lock_is_held_type+0x8f/0x100
worker_thread+0x5ad/0xfd0
__kthread_parkme+0xc6/0x200
kthread+0x31e/0x410
trace_hardirqs_on+0x1a/0x170
ret_from_fork+0x576/0x810
__switch_to+0x57e/0xe20
__switch_to_asm+0x33/0x70
ret_from_fork_asm+0x1a/0x30

Fixes: bd2522b16884 ("cfg80211: NL80211_ATTR_SOCKET_OWNER support for CMD_CONNECT")
Assisted-by: Codex:gpt-5.5
Signed-off-by: Cen Zhang <zzzccc427@xxxxxxxxx>
---
net/wireless/nl80211.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 53b4b3f76697..1e30673d1e79 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -22920,6 +22920,11 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
if (state != NETLINK_URELEASE || notify->protocol != NETLINK_GENERIC)
return NOTIFY_DONE;

+ /*
+ * Keep disconnect_wk scheduling ordered with NETDEV_GOING_DOWN's
+ * cancel_work_sync() and NETDEV_UNREGISTER's wdev list removal.
+ */
+ rtnl_lock();
rcu_read_lock();

list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
@@ -22961,6 +22966,7 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
}

rcu_read_unlock();
+ rtnl_unlock();

/*
* It is possible that the user space process that is controlling the
--
2.43.0