[PATCH wireless-next 1/2] wifi: nxpwifi: protect sta_list against concurrent add and delete

Linmao Li <[email protected]>
Newsgroups org.kernel.vger.linux-wireless,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
nxpwifi_del_sta_entry() unlinks an entry from priv->sta_list without
holding priv->sta_list_spinlock.  The comment above it gives the
reason: the function is only reached from cfg80211_ops.del_station,
which runs under the wiphy mutex.

That mutex serialises this callback against other users of the same
lock, including wiphy_work items.  It covers neither of the two other
writers of the same list:

  nxpwifi_add_sta_entry()    from nxpwifi_uap_event_sta_assoc()
  nxpwifi_del_all_sta_list() from nxpwifi_uap_event_bss_idle()

Both run from nxpwifi_process_event() on the driver's main work queue,
a plain workqueue that never takes the wiphy lock, and both take
sta_list_spinlock around their update.  A station associating, or the
BSS going idle, can therefore modify priv->sta_list while a hostapd
del_station is walking it and unlinking an entry.

Take the lock here too.  As the traversal then runs under the write
lock, list_for_each_entry() replaces the _rcu variant, matching
nxpwifi_del_all_sta_list().

The same call path reaches nxpwifi_get_sta_entry() without
rcu_read_lock().  Entries are freed with kfree_rcu() and every other
caller holds it, so take it around the lookup as well.

Fixes: 73b01e57ed3e ("wifi: nxp: add nxpwifi driver for IW61x")
Signed-off-by: Linmao Li <[email protected]>
---
 drivers/net/wireless/nxp/nxpwifi/cfg80211.c | 2 ++
 drivers/net/wireless/nxp/nxpwifi/util.c     | 8 ++++++--
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c
index 5cc8cdf594d3e..939177bcc6cde 100644
--- a/drivers/net/wireless/nxp/nxpwifi/cfg80211.c
+++ b/drivers/net/wireless/nxp/nxpwifi/cfg80211.c
@@ -1539,9 +1539,11 @@ nxpwifi_cfg80211_del_station(struct wiphy *wiphy, struct wireless_dev *wdev,
 
 	eth_zero_addr(deauth_mac);
 
+	rcu_read_lock();
 	sta_node = nxpwifi_get_sta_entry(priv, params->mac);
 	if (sta_node)
 		ether_addr_copy(deauth_mac, params->mac);
+	rcu_read_unlock();
 
 	if (is_valid_ether_addr(deauth_mac)) {
 		ret = nxpwifi_uap_sta_deauth(priv, deauth_mac);
diff --git a/drivers/net/wireless/nxp/nxpwifi/util.c b/drivers/net/wireless/nxp/nxpwifi/util.c
index bbfefb81d8d3b..ae8444a141058 100644
--- a/drivers/net/wireless/nxp/nxpwifi/util.c
+++ b/drivers/net/wireless/nxp/nxpwifi/util.c
@@ -1041,19 +1041,23 @@ nxpwifi_set_sta_ht_cap(struct nxpwifi_private *priv, const u8 *ies,
 	}
 }
 
-/* Delete a station from list; called under cfg80211 mutex. */
+/* Delete a station from list. */
 
 void nxpwifi_del_sta_entry(struct nxpwifi_private *priv, const u8 *mac)
 {
 	struct nxpwifi_sta_node *node;
 
-	list_for_each_entry_rcu(node, &priv->sta_list, list) {
+	spin_lock_bh(&priv->sta_list_spinlock);
+
+	list_for_each_entry(node, &priv->sta_list, list) {
 		if (!memcmp(node->mac_addr, mac, ETH_ALEN)) {
 			list_del_rcu(&node->list);
 			kfree_rcu(node, rcu);
 			break;
 		}
 	}
+
+	spin_unlock_bh(&priv->sta_list_spinlock);
 }
 
 /* Delete all stations from list. */
-- 
2.25.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.