Ver Fonte

cfg80211: fix potential deadlock regression

My big locking cleanups caused a problem by registering the
rfkill instance with the RTNL held, while the callback also
acquires the RTNL. This potentially causes a deadlock since
the two locks used (rfkill mutex and RTNL) can be acquired
in two different orders. Fix this by (un)registering rfkill
without holding the RTNL. This needs to be done after the
device struct is registered, but that can also be done w/o
holding the RTNL.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Johannes Berg há 12 anos atrás
pai
commit
256c90dedf
1 ficheiros alterados com 8 adições e 15 exclusões
  1. 8 15
      net/wireless/core.c

+ 8 - 15
net/wireless/core.c

@@ -555,14 +555,18 @@ int wiphy_register(struct wiphy *wiphy)
 	/* check and set up bitrates */
 	/* check and set up bitrates */
 	ieee80211_set_bitrate_flags(wiphy);
 	ieee80211_set_bitrate_flags(wiphy);
 
 
-	rtnl_lock();
 
 
 	res = device_add(&rdev->wiphy.dev);
 	res = device_add(&rdev->wiphy.dev);
+	if (res)
+		return res;
+
+	res = rfkill_register(rdev->rfkill);
 	if (res) {
 	if (res) {
-		rtnl_unlock();
+		device_del(&rdev->wiphy.dev);
 		return res;
 		return res;
 	}
 	}
 
 
+	rtnl_lock();
 	/* set up regulatory info */
 	/* set up regulatory info */
 	wiphy_regulatory_register(wiphy);
 	wiphy_regulatory_register(wiphy);
 
 
@@ -589,17 +593,6 @@ int wiphy_register(struct wiphy *wiphy)
 
 
 	cfg80211_debugfs_rdev_add(rdev);
 	cfg80211_debugfs_rdev_add(rdev);
 
 
-	res = rfkill_register(rdev->rfkill);
-	if (res) {
-		device_del(&rdev->wiphy.dev);
-
-		debugfs_remove_recursive(rdev->wiphy.debugfsdir);
-		list_del_rcu(&rdev->list);
-		wiphy_regulatory_deregister(wiphy);
-		rtnl_unlock();
-		return res;
-	}
-
 	rdev->wiphy.registered = true;
 	rdev->wiphy.registered = true;
 	rtnl_unlock();
 	rtnl_unlock();
 	return 0;
 	return 0;
@@ -636,11 +629,11 @@ void wiphy_unregister(struct wiphy *wiphy)
 		rtnl_unlock();
 		rtnl_unlock();
 		__count == 0; }));
 		__count == 0; }));
 
 
+	rfkill_unregister(rdev->rfkill);
+
 	rtnl_lock();
 	rtnl_lock();
 	rdev->wiphy.registered = false;
 	rdev->wiphy.registered = false;
 
 
-	rfkill_unregister(rdev->rfkill);
-
 	BUG_ON(!list_empty(&rdev->wdev_list));
 	BUG_ON(!list_empty(&rdev->wdev_list));
 
 
 	/*
 	/*