mac80211: fix a race condition in the cfg80211 scanning code (thx, johill)
SVN-Revision: 17341
This commit is contained in:
parent
b9451d5542
commit
2015ef16b8
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user