cfg80211: check lost scans later, fix bug
authorJohannes Berg <johannes@sipsolutions.net>
Thu, 20 Aug 2009 19:36:16 +0000 (21:36 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 28 Aug 2009 18:40:25 +0000 (14:40 -0400)
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.

Reported-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Tested-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
net/wireless/core.c
net/wireless/core.h
net/wireless/scan.c

index 154e1e294cb99be7651e1c799298facc9a437e7f..9b157caa74fd2d5ab0bcbcbad0d7dda8c0dc91f0 100644 (file)
@@ -664,7 +664,7 @@ static void wdev_cleanup_work(struct work_struct *work)
 
        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(struct notifier_block * nb,
                default:
                        break;
                }
+               break;
+       case NETDEV_DOWN:
                dev_hold(dev);
                schedule_work(&wdev->cleanup_work);
                break;
index f565432ae22f2744b657e670813a0629da46967e..68eaf340d6136e310c50ec8c4a95106e95a72fe0 100644 (file)
@@ -370,7 +370,7 @@ void cfg80211_sme_scan_done(struct net_device *dev);
 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 *
index fe575a24c95c1196d7b8a1fab66d16471193505e..7043de6221abd71c3d91959a4a2f2431a66ad471 100644 (file)
@@ -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;
@@ -26,8 +26,13 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev)
        union iwreq_data wrqu;
 #endif
 
+       ASSERT_RDEV_LOCK(rdev);
+
        request = rdev->scan_req;
 
+       if (!request)
+               return;
+
        dev = request->dev;
 
        /*
@@ -53,7 +58,17 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev)
        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 +79,7 @@ void __cfg80211_scan_done(struct work_struct *wk)
                            scan_done_wk);
 
        cfg80211_lock_rdev(rdev);
-       ___cfg80211_scan_done(rdev);
+       ___cfg80211_scan_done(rdev, false);
        cfg80211_unlock_rdev(rdev);
 }