wifi: cfg80211: process pending events before connect/disconnect requests

cfg80211 reports connect results and disconnects through wdev->event_list,
processed asynchronously by cfg80211_event_work(). The state that
cfg80211_connect() and cfg80211_disconnect() reason about can therefore
lag behind what the driver has already reported.

cfg80211_disconnect() relies on that asynchronous processing on purpose:
when the interface is connected it leaves wdev->u.client.ssid_len alone,
expecting __cfg80211_disconnected() to clear it later:

	if (!wdev->connected)
		wdev->u.client.ssid_len = 0;

If userspace reconnects before that deferred clear has run,
cfg80211_connect() installs the new SSID and the queued EVENT_DISCONNECTED
then wipes it again, together with wdev->connected, wdev->connect_keys and
wdev->conn_owner_nlportid.

The connection result of the new connection is subsequently processed with
ssid_len == 0, so the BSS lookup in __cfg80211_connect_result() cannot
match any BSS and WARN_ON(bss_not_found) triggers, dropping the connection
result and leaving userspace waiting for a connection that is never
reported. The same happens in cfg80211_roamed(). The zero ssid_len
additionally defeats the -EALREADY and connection-owner checks of the
following request, as both are skipped when ssid_len is zero.

Fix this by draining the pending events before the connect and disconnect
handlers inspect the interface state. Both run under the wiphy mutex,
which cfg80211_process_rdev_events() requires, so no new lock ordering is
introduced; this mirrors what cfg80211_change_iface() already does.

Signed-off-by: Huang Xiangping <huangxiangping88@163.com>
Link: https://patch.msgid.link/20260929035649.33538-1-huangxiangping88@163.com
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Huang Xiangping 2026-09-29 11:56:49 +08:00 committed by Johannes Berg
parent 34f93ba0bc
commit 145e565664

View File

@ -1453,6 +1453,13 @@ int cfg80211_connect(struct cfg80211_registered_device *rdev,
lockdep_assert_wiphy(wdev->wiphy);
/*
* Pending events may still change the state we're about to reason
* about (e.g. a deferred disconnect clearing ssid_len), so process
* them before looking at it.
*/
cfg80211_process_rdev_events(rdev);
/*
* If we have an ssid_len, we're trying to connect or are
* already connected, so reject a new SSID unless it's the
@ -1559,6 +1566,13 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
lockdep_assert_wiphy(wdev->wiphy);
/*
* Pending events may still change the state we're about to reason
* about (e.g. a deferred disconnect clearing ssid_len), so process
* them before looking at it.
*/
cfg80211_process_rdev_events(rdev);
kfree_sensitive(wdev->connect_keys);
wdev->connect_keys = NULL;