mac80211: fix a race condition in the cfg80211 scanning code (thx, johill)
SVN-Revision: 17341
This commit is contained in:
		| @@ -0,0 +1,98 @@ | |||||||
|  | Subject: cfg80211: check lost scans later, fix bug | ||||||
|  |  | ||||||
|  | When we lose a scan, cfg80211 tries to clean up after | ||||||
|  | the driver. However, it currently does this too early, | ||||||
|  | it does this in GOING_DOWN already instead of DOWN, so | ||||||
|  | it may happen with mac80211. Besides fixing this, also | ||||||
|  | make it more robust by leaking the scan request so if | ||||||
|  | the driver later actually finishes the scan, it won't | ||||||
|  | crash. Also check in ___cfg80211_scan_done whether a | ||||||
|  | scan request is still pending and exit if not. | ||||||
|  |  | ||||||
|  | Signed-off-by: Johannes Berg <johannes@sipsolutions.net> | ||||||
|  | --- | ||||||
|  |  net/wireless/core.c |    4 +++- | ||||||
|  |  net/wireless/core.h |    2 +- | ||||||
|  |  net/wireless/scan.c |   19 ++++++++++++++++--- | ||||||
|  |  3 files changed, 20 insertions(+), 5 deletions(-) | ||||||
|  |  | ||||||
|  | --- a/net/wireless/core.c | ||||||
|  | +++ b/net/wireless/core.c | ||||||
|  | @@ -664,7 +664,7 @@ static void wdev_cleanup_work(struct wor | ||||||
|  |   | ||||||
|  |  	if (WARN_ON(rdev->scan_req && rdev->scan_req->dev == wdev->netdev)) { | ||||||
|  |  		rdev->scan_req->aborted = true; | ||||||
|  | -		___cfg80211_scan_done(rdev); | ||||||
|  | +		___cfg80211_scan_done(rdev, true); | ||||||
|  |  	} | ||||||
|  |   | ||||||
|  |  	cfg80211_unlock_rdev(rdev); | ||||||
|  | @@ -755,6 +755,8 @@ static int cfg80211_netdev_notifier_call | ||||||
|  |  		default: | ||||||
|  |  			break; | ||||||
|  |  		} | ||||||
|  | +		break; | ||||||
|  | +	case NETDEV_DOWN: | ||||||
|  |  		dev_hold(dev); | ||||||
|  |  		schedule_work(&wdev->cleanup_work); | ||||||
|  |  		break; | ||||||
|  | --- a/net/wireless/core.h | ||||||
|  | +++ b/net/wireless/core.h | ||||||
|  | @@ -374,7 +374,7 @@ void cfg80211_sme_scan_done(struct net_d | ||||||
|  |  void cfg80211_sme_rx_auth(struct net_device *dev, const u8 *buf, size_t len); | ||||||
|  |  void cfg80211_sme_disassoc(struct net_device *dev, int idx); | ||||||
|  |  void __cfg80211_scan_done(struct work_struct *wk); | ||||||
|  | -void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev); | ||||||
|  | +void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); | ||||||
|  |  void cfg80211_upload_connect_keys(struct wireless_dev *wdev); | ||||||
|  |   | ||||||
|  |  struct ieee80211_channel * | ||||||
|  | --- a/net/wireless/scan.c | ||||||
|  | +++ b/net/wireless/scan.c | ||||||
|  | @@ -18,7 +18,7 @@ | ||||||
|  |   | ||||||
|  |  #define IEEE80211_SCAN_RESULT_EXPIRE	(15 * HZ) | ||||||
|  |   | ||||||
|  | -void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev) | ||||||
|  | +void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) | ||||||
|  |  { | ||||||
|  |  	struct cfg80211_scan_request *request; | ||||||
|  |  	struct net_device *dev; | ||||||
|  | @@ -28,6 +28,9 @@ void ___cfg80211_scan_done(struct cfg802 | ||||||
|  |   | ||||||
|  |  	request = rdev->scan_req; | ||||||
|  |   | ||||||
|  | +	if (!request) | ||||||
|  | +		return; | ||||||
|  | + | ||||||
|  |  	dev = request->dev; | ||||||
|  |   | ||||||
|  |  	/* | ||||||
|  | @@ -53,7 +56,17 @@ void ___cfg80211_scan_done(struct cfg802 | ||||||
|  |  	dev_put(dev); | ||||||
|  |   | ||||||
|  |  	rdev->scan_req = NULL; | ||||||
|  | -	kfree(request); | ||||||
|  | + | ||||||
|  | +	/* | ||||||
|  | +	 * OK. If this is invoked with "leak" then we can't | ||||||
|  | +	 * free this ... but we've cleaned it up anyway. The | ||||||
|  | +	 * driver failed to call the scan_done callback, so | ||||||
|  | +	 * all bets are off, it might still be trying to use | ||||||
|  | +	 * the scan request or not ... if it accesses the dev | ||||||
|  | +	 * in there (it shouldn't anyway) then it may crash. | ||||||
|  | +	 */ | ||||||
|  | +	if (!leak) | ||||||
|  | +		kfree(request); | ||||||
|  |  } | ||||||
|  |   | ||||||
|  |  void __cfg80211_scan_done(struct work_struct *wk) | ||||||
|  | @@ -64,7 +77,7 @@ void __cfg80211_scan_done(struct work_st | ||||||
|  |  			    scan_done_wk); | ||||||
|  |   | ||||||
|  |  	cfg80211_lock_rdev(rdev); | ||||||
|  | -	___cfg80211_scan_done(rdev); | ||||||
|  | +	___cfg80211_scan_done(rdev, false); | ||||||
|  |  	cfg80211_unlock_rdev(rdev); | ||||||
|  |  } | ||||||
|  |   | ||||||
		Reference in New Issue
	
	Block a user
	 Felix Fietkau
					Felix Fietkau