wifi: cfg80211: check RTNL when iterating devices

Add a new "for_each_rdev()" macro and check that we
hold the RTNL when calling it.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Johannes Berg 2023-08-28 13:59:32 +02:00
parent e911a8192e
commit 7483a2147a
6 changed files with 27 additions and 17 deletions

View File

@ -823,7 +823,7 @@ bool cfg80211_any_wiphy_oper_chan(struct wiphy *wiphy,
if (!(chan->flags & IEEE80211_CHAN_RADAR)) if (!(chan->flags & IEEE80211_CHAN_RADAR))
return false; return false;
list_for_each_entry(rdev, &cfg80211_rdev_list, list) { for_each_rdev(rdev) {
if (!reg_dfs_domain_same(wiphy, &rdev->wiphy)) if (!reg_dfs_domain_same(wiphy, &rdev->wiphy))
continue; continue;

View File

@ -60,7 +60,7 @@ struct cfg80211_registered_device *cfg80211_rdev_by_wiphy_idx(int wiphy_idx)
ASSERT_RTNL(); ASSERT_RTNL();
list_for_each_entry(rdev, &cfg80211_rdev_list, list) { for_each_rdev(rdev) {
if (rdev->wiphy_idx == wiphy_idx) { if (rdev->wiphy_idx == wiphy_idx) {
result = rdev; result = rdev;
break; break;
@ -116,7 +116,7 @@ static int cfg80211_dev_check_name(struct cfg80211_registered_device *rdev,
} }
/* Ensure another device does not already have this name. */ /* Ensure another device does not already have this name. */
list_for_each_entry(rdev2, &cfg80211_rdev_list, list) for_each_rdev(rdev2)
if (strcmp(newname, wiphy_name(&rdev2->wiphy)) == 0) if (strcmp(newname, wiphy_name(&rdev2->wiphy)) == 0)
return -EINVAL; return -EINVAL;
@ -1601,7 +1601,7 @@ static void __net_exit cfg80211_pernet_exit(struct net *net)
struct cfg80211_registered_device *rdev; struct cfg80211_registered_device *rdev;
rtnl_lock(); rtnl_lock();
list_for_each_entry(rdev, &cfg80211_rdev_list, list) { for_each_rdev(rdev) {
if (net_eq(wiphy_net(&rdev->wiphy), net)) if (net_eq(wiphy_net(&rdev->wiphy), net))
WARN_ON(cfg80211_switch_netns(rdev, &init_net)); WARN_ON(cfg80211_switch_netns(rdev, &init_net));
} }

View File

@ -160,6 +160,16 @@ extern struct workqueue_struct *cfg80211_wq;
extern struct list_head cfg80211_rdev_list; extern struct list_head cfg80211_rdev_list;
extern int cfg80211_rdev_list_generation; extern int cfg80211_rdev_list_generation;
/* This is constructed like this so it can be used in if/else */
static inline int for_each_rdev_check_rtnl(void)
{
ASSERT_RTNL();
return 0;
}
#define for_each_rdev(rdev) \
if (for_each_rdev_check_rtnl()) {} else \
list_for_each_entry(rdev, &cfg80211_rdev_list, list)
struct cfg80211_internal_bss { struct cfg80211_internal_bss {
struct list_head list; struct list_head list;
struct list_head hidden_list; struct list_head hidden_list;

View File

@ -106,7 +106,7 @@ __cfg80211_wdev_from_attrs(struct cfg80211_registered_device *rdev,
ASSERT_RTNL(); ASSERT_RTNL();
list_for_each_entry(rdev, &cfg80211_rdev_list, list) { for_each_rdev(rdev) {
struct wireless_dev *wdev; struct wireless_dev *wdev;
if (wiphy_net(&rdev->wiphy) != netns) if (wiphy_net(&rdev->wiphy) != netns)
@ -3075,7 +3075,7 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
cb->args[0] = (long)state; cb->args[0] = (long)state;
} }
list_for_each_entry(rdev, &cfg80211_rdev_list, list) { for_each_rdev(rdev) {
if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk))) if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
continue; continue;
if (++idx <= state->start) if (++idx <= state->start)
@ -3985,7 +3985,7 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *
filter_wiphy = cb->args[2] - 1; filter_wiphy = cb->args[2] - 1;
} }
list_for_each_entry(rdev, &cfg80211_rdev_list, list) { for_each_rdev(rdev) {
if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk))) if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
continue; continue;
if (wp_idx < wp_start) { if (wp_idx < wp_start) {

View File

@ -2461,7 +2461,7 @@ static void reg_check_chans_work(struct work_struct *work)
pr_debug("Verifying active interfaces after reg change\n"); pr_debug("Verifying active interfaces after reg change\n");
rtnl_lock(); rtnl_lock();
list_for_each_entry(rdev, &cfg80211_rdev_list, list) for_each_rdev(rdev)
reg_leave_invalid_chans(&rdev->wiphy); reg_leave_invalid_chans(&rdev->wiphy);
rtnl_unlock(); rtnl_unlock();
@ -2515,7 +2515,7 @@ static void update_all_wiphy_regulatory(enum nl80211_reg_initiator initiator)
ASSERT_RTNL(); ASSERT_RTNL();
list_for_each_entry(rdev, &cfg80211_rdev_list, list) { for_each_rdev(rdev) {
wiphy = &rdev->wiphy; wiphy = &rdev->wiphy;
wiphy_update_regulatory(wiphy, initiator); wiphy_update_regulatory(wiphy, initiator);
} }
@ -2991,7 +2991,7 @@ static void wiphy_all_share_dfs_chan_state(struct wiphy *wiphy)
ASSERT_RTNL(); ASSERT_RTNL();
list_for_each_entry(rdev, &cfg80211_rdev_list, list) { for_each_rdev(rdev) {
if (wiphy == &rdev->wiphy) if (wiphy == &rdev->wiphy)
continue; continue;
wiphy_share_dfs_chan_state(wiphy, &rdev->wiphy); wiphy_share_dfs_chan_state(wiphy, &rdev->wiphy);
@ -3057,7 +3057,7 @@ static void notify_self_managed_wiphys(struct regulatory_request *request)
struct cfg80211_registered_device *rdev; struct cfg80211_registered_device *rdev;
struct wiphy *wiphy; struct wiphy *wiphy;
list_for_each_entry(rdev, &cfg80211_rdev_list, list) { for_each_rdev(rdev) {
wiphy = &rdev->wiphy; wiphy = &rdev->wiphy;
if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED && if (wiphy->regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED &&
request->initiator == NL80211_REGDOM_SET_BY_USER) request->initiator == NL80211_REGDOM_SET_BY_USER)
@ -3122,7 +3122,7 @@ static void reg_process_pending_beacon_hints(void)
list_del_init(&pending_beacon->list); list_del_init(&pending_beacon->list);
/* Applies the beacon hint to current wiphys */ /* Applies the beacon hint to current wiphys */
list_for_each_entry(rdev, &cfg80211_rdev_list, list) for_each_rdev(rdev)
wiphy_update_new_beacon(&rdev->wiphy, pending_beacon); wiphy_update_new_beacon(&rdev->wiphy, pending_beacon);
/* Remembers the beacon hint for new wiphys or reg changes */ /* Remembers the beacon hint for new wiphys or reg changes */
@ -3177,7 +3177,7 @@ static void reg_process_self_managed_hints(void)
ASSERT_RTNL(); ASSERT_RTNL();
list_for_each_entry(rdev, &cfg80211_rdev_list, list) { for_each_rdev(rdev) {
wiphy_lock(&rdev->wiphy); wiphy_lock(&rdev->wiphy);
reg_process_self_managed_hint(&rdev->wiphy); reg_process_self_managed_hint(&rdev->wiphy);
wiphy_unlock(&rdev->wiphy); wiphy_unlock(&rdev->wiphy);
@ -3517,7 +3517,7 @@ static void restore_regulatory_settings(bool reset_user, bool cached)
world_alpha2[0] = cfg80211_world_regdom->alpha2[0]; world_alpha2[0] = cfg80211_world_regdom->alpha2[0];
world_alpha2[1] = cfg80211_world_regdom->alpha2[1]; world_alpha2[1] = cfg80211_world_regdom->alpha2[1];
list_for_each_entry(rdev, &cfg80211_rdev_list, list) { for_each_rdev(rdev) {
if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED) if (rdev->wiphy.regulatory_flags & REGULATORY_WIPHY_SELF_MANAGED)
continue; continue;
if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG) if (rdev->wiphy.regulatory_flags & REGULATORY_CUSTOM_REG)
@ -3574,7 +3574,7 @@ static bool is_wiphy_all_set_reg_flag(enum ieee80211_regulatory_flags flag)
struct cfg80211_registered_device *rdev; struct cfg80211_registered_device *rdev;
struct wireless_dev *wdev; struct wireless_dev *wdev;
list_for_each_entry(rdev, &cfg80211_rdev_list, list) { for_each_rdev(rdev) {
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
wdev_lock(wdev); wdev_lock(wdev);
if (!(wdev->wiphy->regulatory_flags & flag)) { if (!(wdev->wiphy->regulatory_flags & flag)) {
@ -4244,7 +4244,7 @@ void regulatory_propagate_dfs_state(struct wiphy *wiphy,
if (WARN_ON(!cfg80211_chandef_valid(chandef))) if (WARN_ON(!cfg80211_chandef_valid(chandef)))
return; return;
list_for_each_entry(rdev, &cfg80211_rdev_list, list) { for_each_rdev(rdev) {
if (wiphy == &rdev->wiphy) if (wiphy == &rdev->wiphy)
continue; continue;

View File

@ -702,7 +702,7 @@ static bool cfg80211_is_all_idle(void)
* need not issue a disconnect hint and reset any info such * need not issue a disconnect hint and reset any info such
* as chan dfs state, etc. * as chan dfs state, etc.
*/ */
list_for_each_entry(rdev, &cfg80211_rdev_list, list) { for_each_rdev(rdev) {
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) {
wdev_lock(wdev); wdev_lock(wdev);
if (wdev->conn || wdev->connected || if (wdev->conn || wdev->connected ||