wifi: cfg80211: protect wiphy names with cfg80211_mutex

The RTNL is only used for wiphy names to serialize checking
for name collisions and setting the name, across all wiphys.
Since the wiphy list can be walked under the cfg80211_mutex,
use that instead.

In nl80211_set_wiphy() this requires taking it before the
wiphy mutex, and then the RTNL is no longer needed there at
all if the netdev is looked up under RCU. Since the netns of
the wiphy and its netdevs is now also stable under the
cfg80211_mutex, only the netdev unregistering needs to be
handled, by looking up the wdev again under the wiphy mutex.

Assisted-by: LLM
Link: https://patch.msgid.link/20261011113308.6cedefb201f4.I03ffe79f337b61ea8f91b91532d3dfb9497e0438@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2026-10-11 11:31:29 +02:00
parent 65460860e1
commit ef27674b84
2 changed files with 37 additions and 20 deletions

View File

@ -94,7 +94,7 @@ static int cfg80211_dev_check_name(struct cfg80211_registered_device *rdev,
struct cfg80211_registered_device *rdev2;
int wiphy_idx, taken = -1, digits;
ASSERT_RTNL();
lockdep_assert_held(&cfg80211_mutex);
if (strlen(newname) > NL80211_WIPHY_NAME_MAXLEN)
return -EINVAL;
@ -127,7 +127,7 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
{
int result;
ASSERT_RTNL();
lockdep_assert_held(&cfg80211_mutex);
lockdep_assert_wiphy(&rdev->wiphy);
/* Ignore nop renames */
@ -664,16 +664,16 @@ struct wiphy *wiphy_new_nm(const struct cfg80211_ops *ops, int sizeof_priv,
if (requested_name && requested_name[0]) {
int rv;
rtnl_lock();
mutex_lock(&cfg80211_mutex);
rv = cfg80211_dev_check_name(rdev, requested_name);
if (rv < 0) {
rtnl_unlock();
mutex_unlock(&cfg80211_mutex);
goto use_default_name;
}
rv = dev_set_name(&rdev->wiphy.dev, "%s", requested_name);
rtnl_unlock();
mutex_unlock(&cfg80211_mutex);
if (rv)
goto use_default_name;
} else {

View File

@ -4372,17 +4372,18 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = NULL;
struct net_device *netdev = NULL;
struct wireless_dev *wdev;
struct wireless_dev *wdev = NULL;
int result = 0, rem_txq_params = 0;
struct nlattr *nl_txq_params;
u32 changed;
u32 changed, wdev_id = 0;
u8 retry_short = 0, retry_long = 0;
u32 frag_threshold = 0, rts_threshold = 0;
u8 coverage_class = 0;
u32 txq_limit = 0, txq_memory_limit = 0, txq_quantum = 0;
int radio_idx = -1;
rtnl_lock();
/* keeps the wiphy registered and its netns stable */
mutex_lock(&cfg80211_mutex);
/*
* Try to find the wiphy and netdev. Normally this
* function shouldn't need the netdev, but this is
@ -4396,28 +4397,44 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_IFINDEX]) {
int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
netdev = __dev_get_by_index(genl_info_net(info), ifindex);
if (netdev && netdev->ieee80211_ptr)
rcu_read_lock();
netdev = dev_get_by_index_rcu(genl_info_net(info), ifindex);
if (netdev && netdev->ieee80211_ptr) {
rdev = wiphy_to_rdev(netdev->ieee80211_ptr->wiphy);
else
netdev = NULL;
wdev_id = netdev->ieee80211_ptr->identifier;
}
rcu_read_unlock();
}
if (!netdev) {
if (!rdev) {
/* RCU for the wdev list walk, cfg80211_mutex keeps rdev */
rcu_read_lock();
rdev = __cfg80211_rdev_from_attrs(genl_info_net(info),
info->attrs);
rcu_read_unlock();
if (IS_ERR(rdev)) {
rtnl_unlock();
mutex_unlock(&cfg80211_mutex);
return PTR_ERR(rdev);
}
wdev = NULL;
netdev = NULL;
result = 0;
} else
wdev = netdev->ieee80211_ptr;
}
guard(wiphy)(&rdev->wiphy);
/* the netdev may have been unregistered after the lookup */
if (wdev_id) {
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
if (wdev->identifier == wdev_id)
break;
}
if (list_entry_is_head(wdev, &rdev->wiphy.wdev_list, list)) {
mutex_unlock(&cfg80211_mutex);
return -ENODEV;
}
netdev = wdev->netdev;
} else {
netdev = NULL;
}
/*
* end workaround code, by now the rdev is available
* and locked, and wdev may or may not be NULL.
@ -4426,7 +4443,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
if (info->attrs[NL80211_ATTR_WIPHY_NAME])
result = cfg80211_dev_rename(
rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
rtnl_unlock();
mutex_unlock(&cfg80211_mutex);
if (result)
return result;