summaryrefslogtreecommitdiff
path: root/package/mac80211
diff options
context:
space:
mode:
authornbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73>2009-08-20 18:49:12 +0000
committernbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73>2009-08-20 18:49:12 +0000
commit1a4a2b5ae76b2c685d4142402e1190431af629b8 (patch)
tree0047cb5a8d325d4032354dcfca2c51e60f54850d /package/mac80211
parentf6b146de59bdcb8a3380fef950688eb9b8b41b31 (diff)
mac80211: fix a race condition in the cfg80211 scanning code (thx, johill)
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@17341 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'package/mac80211')
-rw-r--r--package/mac80211/patches/100-cfg80211_fix_scan_check_again.patch98
1 files changed, 98 insertions, 0 deletions
diff --git a/package/mac80211/patches/100-cfg80211_fix_scan_check_again.patch b/package/mac80211/patches/100-cfg80211_fix_scan_check_again.patch
new file mode 100644
index 0000000000..77b795c124
--- /dev/null
+++ b/package/mac80211/patches/100-cfg80211_fix_scan_check_again.patch
@@ -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);
+ }
+