cfg80211: fix netdev registration deadlock
If register_netdevice() fails after having called cfg80211's
netdev notifier (cfg80211_netdev_notifier_call) it will call
the notifier again with UNREGISTER. This would then lock the
wiphy mutex because we're marked as registered, which causes
a deadlock.
Fix this by separately keeping track of whether or not we're
in the middle of registering to also skip the notifier call
on this unregister.
Reported-by: syzbot+2ae0ca9d7737ad1a62b7@syzkaller.appspotmail.com
Fixes: a05829a722
("cfg80211: avoid holding the RTNL when calling the driver")
Link: https://lore.kernel.org/r/20210201192048.ed8bad436737.I7cae042c44b15f80919a285799a15df467e9d42d@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
776a39b819
commit
40c575d1ec
@ -5299,6 +5299,8 @@ static inline void wiphy_unlock(struct wiphy *wiphy)
|
|||||||
* @wiphy: pointer to hardware description
|
* @wiphy: pointer to hardware description
|
||||||
* @iftype: interface type
|
* @iftype: interface type
|
||||||
* @registered: is this wdev already registered with cfg80211
|
* @registered: is this wdev already registered with cfg80211
|
||||||
|
* @registering: indicates we're doing registration under wiphy lock
|
||||||
|
* for the notifier
|
||||||
* @list: (private) Used to collect the interfaces
|
* @list: (private) Used to collect the interfaces
|
||||||
* @netdev: (private) Used to reference back to the netdev, may be %NULL
|
* @netdev: (private) Used to reference back to the netdev, may be %NULL
|
||||||
* @identifier: (private) Identifier used in nl80211 to identify this
|
* @identifier: (private) Identifier used in nl80211 to identify this
|
||||||
@ -5382,7 +5384,7 @@ struct wireless_dev {
|
|||||||
|
|
||||||
struct mutex mtx;
|
struct mutex mtx;
|
||||||
|
|
||||||
bool use_4addr, is_running, registered;
|
bool use_4addr, is_running, registered, registering;
|
||||||
|
|
||||||
u8 address[ETH_ALEN] __aligned(sizeof(u16));
|
u8 address[ETH_ALEN] __aligned(sizeof(u16));
|
||||||
|
|
||||||
|
@ -1346,6 +1346,7 @@ int cfg80211_register_netdevice(struct net_device *dev)
|
|||||||
|
|
||||||
/* we'll take care of this */
|
/* we'll take care of this */
|
||||||
wdev->registered = true;
|
wdev->registered = true;
|
||||||
|
wdev->registering = true;
|
||||||
ret = register_netdevice(dev);
|
ret = register_netdevice(dev);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
@ -1361,6 +1362,7 @@ int cfg80211_register_netdevice(struct net_device *dev)
|
|||||||
cfg80211_register_wdev(rdev, wdev);
|
cfg80211_register_wdev(rdev, wdev);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
out:
|
out:
|
||||||
|
wdev->registering = false;
|
||||||
if (ret)
|
if (ret)
|
||||||
wdev->registered = false;
|
wdev->registered = false;
|
||||||
return ret;
|
return ret;
|
||||||
@ -1403,7 +1405,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
|
|||||||
* It is possible to get NETDEV_UNREGISTER multiple times,
|
* It is possible to get NETDEV_UNREGISTER multiple times,
|
||||||
* so check wdev->registered.
|
* so check wdev->registered.
|
||||||
*/
|
*/
|
||||||
if (wdev->registered) {
|
if (wdev->registered && !wdev->registering) {
|
||||||
wiphy_lock(&rdev->wiphy);
|
wiphy_lock(&rdev->wiphy);
|
||||||
_cfg80211_unregister_wdev(wdev, false);
|
_cfg80211_unregister_wdev(wdev, false);
|
||||||
wiphy_unlock(&rdev->wiphy);
|
wiphy_unlock(&rdev->wiphy);
|
||||||
|
Loading…
Reference in New Issue
Block a user