Commit 40c575d1 authored by Johannes Berg's avatar Johannes Berg
Browse files

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: default avatar <syzbot+2ae0ca9d7737ad1a62b7@syzkaller.appspotmail.com>
Fixes: a05829a7 ("cfg80211: avoid holding the RTNL when calling the driver")
Link: https://lore.kernel.org/r/20210201192048.ed8bad436737.I7cae042c44b15f80919a285799a15df467e9d42d@changeid


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 776a39b8
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -5299,6 +5299,8 @@ static inline void wiphy_unlock(struct wiphy *wiphy)
 * @wiphy: pointer to hardware description
 * @iftype: interface type
 * @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
 * @netdev: (private) Used to reference back to the netdev, may be %NULL
 * @identifier: (private) Identifier used in nl80211 to identify this
@@ -5382,7 +5384,7 @@ struct wireless_dev {

	struct mutex mtx;

	bool use_4addr, is_running, registered;
	bool use_4addr, is_running, registered, registering;

	u8 address[ETH_ALEN] __aligned(sizeof(u16));

+3 −1
Original line number Diff line number Diff line
@@ -1346,6 +1346,7 @@ int cfg80211_register_netdevice(struct net_device *dev)

	/* we'll take care of this */
	wdev->registered = true;
	wdev->registering = true;
	ret = register_netdevice(dev);
	if (ret)
		goto out;
@@ -1361,6 +1362,7 @@ int cfg80211_register_netdevice(struct net_device *dev)
	cfg80211_register_wdev(rdev, wdev);
	ret = 0;
out:
	wdev->registering = false;
	if (ret)
		wdev->registered = false;
	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,
		 * so check wdev->registered.
		 */
		if (wdev->registered) {
		if (wdev->registered && !wdev->registering) {
			wiphy_lock(&rdev->wiphy);
			_cfg80211_unregister_wdev(wdev, false);
			wiphy_unlock(&rdev->wiphy);