Commit 20658702 authored by Arik Nemtsov's avatar Arik Nemtsov Committed by Johannes Berg

cfg80211: fix deadlock during reg chan check

If a P2P GO is active, the cfg80211_reg_can_beacon function will take
the wdev lock, in its call to cfg80211_go_permissive_chan. But the wdev lock
is already taken by the parent channel-checking function, causing a
deadlock.
Split the checking code into two parts. The first part will check if the
wdev is active and saves the channel under the wdev lock. The second part
will check actual channel validity according to type.
Signed-off-by: default avatarArik Nemtsov <arikx.nemtsov@intel.com>
Reviewed-by: default avatarIlan Peer <ilan.peer@intel.com>
Reviewed-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent cc72f6e2
...@@ -1530,45 +1530,40 @@ static void reg_call_notifier(struct wiphy *wiphy, ...@@ -1530,45 +1530,40 @@ static void reg_call_notifier(struct wiphy *wiphy,
static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
{ {
struct ieee80211_channel *ch;
struct cfg80211_chan_def chandef; struct cfg80211_chan_def chandef;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy); struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
bool ret = true; enum nl80211_iftype iftype;
wdev_lock(wdev); wdev_lock(wdev);
iftype = wdev->iftype;
/* make sure the interface is active */
if (!wdev->netdev || !netif_running(wdev->netdev)) if (!wdev->netdev || !netif_running(wdev->netdev))
goto out; goto wdev_inactive_unlock;
switch (wdev->iftype) { switch (iftype) {
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_P2P_GO:
if (!wdev->beacon_interval) if (!wdev->beacon_interval)
goto out; goto wdev_inactive_unlock;
chandef = wdev->chandef;
ret = cfg80211_reg_can_beacon(wiphy,
&wdev->chandef, wdev->iftype);
break; break;
case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_ADHOC:
if (!wdev->ssid_len) if (!wdev->ssid_len)
goto out; goto wdev_inactive_unlock;
chandef = wdev->chandef;
ret = cfg80211_reg_can_beacon(wiphy,
&wdev->chandef, wdev->iftype);
break; break;
case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_P2P_CLIENT:
if (!wdev->current_bss || if (!wdev->current_bss ||
!wdev->current_bss->pub.channel) !wdev->current_bss->pub.channel)
goto out; goto wdev_inactive_unlock;
ch = wdev->current_bss->pub.channel; if (!rdev->ops->get_channel ||
if (rdev->ops->get_channel && rdev_get_channel(rdev, wdev, &chandef))
!rdev_get_channel(rdev, wdev, &chandef)) cfg80211_chandef_create(&chandef,
ret = cfg80211_chandef_usable(wiphy, &chandef, wdev->current_bss->pub.channel,
IEEE80211_CHAN_DISABLED); NL80211_CHAN_NO_HT);
else
ret = !(ch->flags & IEEE80211_CHAN_DISABLED);
break; break;
case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_MONITOR:
case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_AP_VLAN:
...@@ -1581,9 +1576,26 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev) ...@@ -1581,9 +1576,26 @@ static bool reg_wdev_chan_valid(struct wiphy *wiphy, struct wireless_dev *wdev)
break; break;
} }
out:
wdev_unlock(wdev); wdev_unlock(wdev);
return ret;
switch (iftype) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
case NL80211_IFTYPE_ADHOC:
return cfg80211_reg_can_beacon(wiphy, &chandef, iftype);
case NL80211_IFTYPE_STATION:
case NL80211_IFTYPE_P2P_CLIENT:
return cfg80211_chandef_usable(wiphy, &chandef,
IEEE80211_CHAN_DISABLED);
default:
break;
}
return true;
wdev_inactive_unlock:
wdev_unlock(wdev);
return true;
} }
static void reg_leave_invalid_chans(struct wiphy *wiphy) static void reg_leave_invalid_chans(struct wiphy *wiphy)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment