aboutsummaryrefslogtreecommitdiffstats
path: root/net/wireless/core.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-03-19 10:04:07 -0400
committerJohannes Berg <johannes.berg@intel.com>2013-03-24 06:15:58 -0400
commitf9f475292dbb0e7035fb6661d1524761ea0888d9 (patch)
tree77f4bf7741deb74108ef033eeb68131069a8e406 /net/wireless/core.c
parent8b305780ed0c49a49c6bd58a4372fd6b22a5a71e (diff)
cfg80211: always check for scan end on P2P device
If a P2P device wdev is removed while it has a scan, then the scan completion might crash later as it is already freed by that time. To avoid the crash always check the scan completion when the P2P device is being removed for some reason. If the driver already canceled it, don't want and free it, otherwise warn and leak it to avoid later crashes. In order to do this, locking needs to be changed away from the rdev mutex (which can't always be guaranteed). For now, use the sched_scan_mtx instead, I'll rename it to just scan_mtx in a later patch. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless/core.c')
-rw-r--r--net/wireless/core.c64
1 files changed, 47 insertions, 17 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 922002105062..11743d48cbc2 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -212,6 +212,39 @@ static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data)
212 rdev_rfkill_poll(rdev); 212 rdev_rfkill_poll(rdev);
213} 213}
214 214
215void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
216 struct wireless_dev *wdev)
217{
218 lockdep_assert_held(&rdev->devlist_mtx);
219 lockdep_assert_held(&rdev->sched_scan_mtx);
220
221 if (WARN_ON(wdev->iftype != NL80211_IFTYPE_P2P_DEVICE))
222 return;
223
224 if (!wdev->p2p_started)
225 return;
226
227 rdev_stop_p2p_device(rdev, wdev);
228 wdev->p2p_started = false;
229
230 rdev->opencount--;
231
232 if (rdev->scan_req && rdev->scan_req->wdev == wdev) {
233 bool busy = work_busy(&rdev->scan_done_wk);
234
235 /*
236 * If the work isn't pending or running (in which case it would
237 * be waiting for the lock we hold) the driver didn't properly
238 * cancel the scan when the interface was removed. In this case
239 * warn and leak the scan request object to not crash later.
240 */
241 WARN_ON(!busy);
242
243 rdev->scan_req->aborted = true;
244 ___cfg80211_scan_done(rdev, !busy);
245 }
246}
247
215static int cfg80211_rfkill_set_block(void *data, bool blocked) 248static int cfg80211_rfkill_set_block(void *data, bool blocked)
216{ 249{
217 struct cfg80211_registered_device *rdev = data; 250 struct cfg80211_registered_device *rdev = data;
@@ -221,7 +254,8 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked)
221 return 0; 254 return 0;
222 255
223 rtnl_lock(); 256 rtnl_lock();
224 mutex_lock(&rdev->devlist_mtx); 257
258 /* read-only iteration need not hold the devlist_mtx */
225 259
226 list_for_each_entry(wdev, &rdev->wdev_list, list) { 260 list_for_each_entry(wdev, &rdev->wdev_list, list) {
227 if (wdev->netdev) { 261 if (wdev->netdev) {
@@ -231,18 +265,18 @@ static int cfg80211_rfkill_set_block(void *data, bool blocked)
231 /* otherwise, check iftype */ 265 /* otherwise, check iftype */
232 switch (wdev->iftype) { 266 switch (wdev->iftype) {
233 case NL80211_IFTYPE_P2P_DEVICE: 267 case NL80211_IFTYPE_P2P_DEVICE:
234 if (!wdev->p2p_started) 268 /* but this requires it */
235 break; 269 mutex_lock(&rdev->devlist_mtx);
236 rdev_stop_p2p_device(rdev, wdev); 270 mutex_lock(&rdev->sched_scan_mtx);
237 wdev->p2p_started = false; 271 cfg80211_stop_p2p_device(rdev, wdev);
238 rdev->opencount--; 272 mutex_unlock(&rdev->sched_scan_mtx);
273 mutex_unlock(&rdev->devlist_mtx);
239 break; 274 break;
240 default: 275 default:
241 break; 276 break;
242 } 277 }
243 } 278 }
244 279
245 mutex_unlock(&rdev->devlist_mtx);
246 rtnl_unlock(); 280 rtnl_unlock();
247 281
248 return 0; 282 return 0;
@@ -745,17 +779,13 @@ static void wdev_cleanup_work(struct work_struct *work)
745 wdev = container_of(work, struct wireless_dev, cleanup_work); 779 wdev = container_of(work, struct wireless_dev, cleanup_work);
746 rdev = wiphy_to_dev(wdev->wiphy); 780 rdev = wiphy_to_dev(wdev->wiphy);
747 781
748 cfg80211_lock_rdev(rdev); 782 mutex_lock(&rdev->sched_scan_mtx);
749 783
750 if (WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev)) { 784 if (WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev)) {
751 rdev->scan_req->aborted = true; 785 rdev->scan_req->aborted = true;
752 ___cfg80211_scan_done(rdev, true); 786 ___cfg80211_scan_done(rdev, true);
753 } 787 }
754 788
755 cfg80211_unlock_rdev(rdev);
756
757 mutex_lock(&rdev->sched_scan_mtx);
758
759 if (WARN_ON(rdev->sched_scan_req && 789 if (WARN_ON(rdev->sched_scan_req &&
760 rdev->sched_scan_req->dev == wdev->netdev)) { 790 rdev->sched_scan_req->dev == wdev->netdev)) {
761 __cfg80211_stop_sched_scan(rdev, false); 791 __cfg80211_stop_sched_scan(rdev, false);
@@ -781,21 +811,19 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev)
781 return; 811 return;
782 812
783 mutex_lock(&rdev->devlist_mtx); 813 mutex_lock(&rdev->devlist_mtx);
814 mutex_lock(&rdev->sched_scan_mtx);
784 list_del_rcu(&wdev->list); 815 list_del_rcu(&wdev->list);
785 rdev->devlist_generation++; 816 rdev->devlist_generation++;
786 817
787 switch (wdev->iftype) { 818 switch (wdev->iftype) {
788 case NL80211_IFTYPE_P2P_DEVICE: 819 case NL80211_IFTYPE_P2P_DEVICE:
789 if (!wdev->p2p_started) 820 cfg80211_stop_p2p_device(rdev, wdev);
790 break;
791 rdev_stop_p2p_device(rdev, wdev);
792 wdev->p2p_started = false;
793 rdev->opencount--;
794 break; 821 break;
795 default: 822 default:
796 WARN_ON_ONCE(1); 823 WARN_ON_ONCE(1);
797 break; 824 break;
798 } 825 }
826 mutex_unlock(&rdev->sched_scan_mtx);
799 mutex_unlock(&rdev->devlist_mtx); 827 mutex_unlock(&rdev->devlist_mtx);
800} 828}
801EXPORT_SYMBOL(cfg80211_unregister_wdev); 829EXPORT_SYMBOL(cfg80211_unregister_wdev);
@@ -937,6 +965,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
937 cfg80211_update_iface_num(rdev, wdev->iftype, 1); 965 cfg80211_update_iface_num(rdev, wdev->iftype, 1);
938 cfg80211_lock_rdev(rdev); 966 cfg80211_lock_rdev(rdev);
939 mutex_lock(&rdev->devlist_mtx); 967 mutex_lock(&rdev->devlist_mtx);
968 mutex_lock(&rdev->sched_scan_mtx);
940 wdev_lock(wdev); 969 wdev_lock(wdev);
941 switch (wdev->iftype) { 970 switch (wdev->iftype) {
942#ifdef CONFIG_CFG80211_WEXT 971#ifdef CONFIG_CFG80211_WEXT
@@ -968,6 +997,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
968 break; 997 break;
969 } 998 }
970 wdev_unlock(wdev); 999 wdev_unlock(wdev);
1000 mutex_unlock(&rdev->sched_scan_mtx);
971 rdev->opencount++; 1001 rdev->opencount++;
972 mutex_unlock(&rdev->devlist_mtx); 1002 mutex_unlock(&rdev->devlist_mtx);
973 cfg80211_unlock_rdev(rdev); 1003 cfg80211_unlock_rdev(rdev);