aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEliad Peller <eliad@wizery.com>2013-12-05 11:30:17 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-12-05 13:06:47 -0500
commit4a58e7c38443154fce1b47910e1a9184f65c5d72 (patch)
tree312b02efaf0de0522cca0eebfac2b5b861430d68
parenta2b70e833e189a4aefb2d3b668e3d7046dcc45c2 (diff)
cfg80211: don't "leak" uncompleted scans
___cfg80211_scan_done() can be called in some cases (e.g. on NETDEV_DOWN) before the low level driver notified scan completion (which is indicated by passing leak=true). Clearing rdev->scan_req in this case is buggy, as scan_done_wk might have already being queued/running (and can't be flushed as it takes rtnl()). If a new scan will be requested at this stage, the scan_done_wk will try freeing it (instead of the previous scan), and this will later result in a use after free. Simply remove the "leak" option, and replace it with a standard WARN_ON. An example backtrace after such crash: Unable to handle kernel paging request at virtual address fffffee5 pgd = c0004000 [fffffee5] *pgd=9fdf6821, *pte=00000000, *ppte=00000000 Internal error: Oops: 17 [#1] SMP ARM PC is at cfg80211_scan_done+0x28/0xc4 [cfg80211] LR is at __ieee80211_scan_completed+0xe4/0x2dc [mac80211] [<bf0077b0>] (cfg80211_scan_done+0x28/0xc4 [cfg80211]) [<bf0973d4>] (__ieee80211_scan_completed+0xe4/0x2dc [mac80211]) [<bf0982cc>] (ieee80211_scan_work+0x94/0x4f0 [mac80211]) [<c005fd10>] (process_one_work+0x1b0/0x4a8) [<c0060404>] (worker_thread+0x138/0x37c) [<c0066d70>] (kthread+0xa4/0xb0) Signed-off-by: Eliad Peller <eliad@wizery.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--net/wireless/core.c20
-rw-r--r--net/wireless/core.h2
-rw-r--r--net/wireless/scan.c16
3 files changed, 8 insertions, 30 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c
index c0443558b7bf..1a92c6a0731f 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -203,17 +203,8 @@ void cfg80211_stop_p2p_device(struct cfg80211_registered_device *rdev,
203 203
204 rdev->opencount--; 204 rdev->opencount--;
205 205
206 if (rdev->scan_req && rdev->scan_req->wdev == wdev) { 206 WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev &&
207 /* 207 !rdev->scan_req->notified);
208 * If the scan request wasn't notified as done, set it
209 * to aborted and leak it after a warning. The driver
210 * should have notified us that it ended at the latest
211 * during rdev_stop_p2p_device().
212 */
213 if (WARN_ON(!rdev->scan_req->notified))
214 rdev->scan_req->aborted = true;
215 ___cfg80211_scan_done(rdev, !rdev->scan_req->notified);
216 }
217} 208}
218 209
219static int cfg80211_rfkill_set_block(void *data, bool blocked) 210static int cfg80211_rfkill_set_block(void *data, bool blocked)
@@ -859,11 +850,8 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
859 break; 850 break;
860 case NETDEV_DOWN: 851 case NETDEV_DOWN:
861 cfg80211_update_iface_num(rdev, wdev->iftype, -1); 852 cfg80211_update_iface_num(rdev, wdev->iftype, -1);
862 if (rdev->scan_req && rdev->scan_req->wdev == wdev) { 853 WARN_ON(rdev->scan_req && rdev->scan_req->wdev == wdev &&
863 if (WARN_ON(!rdev->scan_req->notified)) 854 !rdev->scan_req->notified);
864 rdev->scan_req->aborted = true;
865 ___cfg80211_scan_done(rdev, true);
866 }
867 855
868 if (WARN_ON(rdev->sched_scan_req && 856 if (WARN_ON(rdev->sched_scan_req &&
869 rdev->sched_scan_req->dev == wdev->netdev)) { 857 rdev->sched_scan_req->dev == wdev->netdev)) {
diff --git a/net/wireless/core.h b/net/wireless/core.h
index ac77644cf894..453c6ed880f1 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -361,7 +361,7 @@ int cfg80211_validate_key_settings(struct cfg80211_registered_device *rdev,
361 struct key_params *params, int key_idx, 361 struct key_params *params, int key_idx,
362 bool pairwise, const u8 *mac_addr); 362 bool pairwise, const u8 *mac_addr);
363void __cfg80211_scan_done(struct work_struct *wk); 363void __cfg80211_scan_done(struct work_struct *wk);
364void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak); 364void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev);
365void __cfg80211_sched_scan_results(struct work_struct *wk); 365void __cfg80211_sched_scan_results(struct work_struct *wk);
366int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev, 366int __cfg80211_stop_sched_scan(struct cfg80211_registered_device *rdev,
367 bool driver_initiated); 367 bool driver_initiated);
diff --git a/net/wireless/scan.c b/net/wireless/scan.c
index d4397eba5408..a32d52a04c27 100644
--- a/net/wireless/scan.c
+++ b/net/wireless/scan.c
@@ -161,7 +161,7 @@ static void __cfg80211_bss_expire(struct cfg80211_registered_device *dev,
161 dev->bss_generation++; 161 dev->bss_generation++;
162} 162}
163 163
164void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak) 164void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev)
165{ 165{
166 struct cfg80211_scan_request *request; 166 struct cfg80211_scan_request *request;
167 struct wireless_dev *wdev; 167 struct wireless_dev *wdev;
@@ -210,17 +210,7 @@ void ___cfg80211_scan_done(struct cfg80211_registered_device *rdev, bool leak)
210 dev_put(wdev->netdev); 210 dev_put(wdev->netdev);
211 211
212 rdev->scan_req = NULL; 212 rdev->scan_req = NULL;
213 213 kfree(request);
214 /*
215 * OK. If this is invoked with "leak" then we can't
216 * free this ... but we've cleaned it up anyway. The
217 * driver failed to call the scan_done callback, so
218 * all bets are off, it might still be trying to use
219 * the scan request or not ... if it accesses the dev
220 * in there (it shouldn't anyway) then it may crash.
221 */
222 if (!leak)
223 kfree(request);
224} 214}
225 215
226void __cfg80211_scan_done(struct work_struct *wk) 216void __cfg80211_scan_done(struct work_struct *wk)
@@ -231,7 +221,7 @@ void __cfg80211_scan_done(struct work_struct *wk)
231 scan_done_wk); 221 scan_done_wk);
232 222
233 rtnl_lock(); 223 rtnl_lock();
234 ___cfg80211_scan_done(rdev, false); 224 ___cfg80211_scan_done(rdev);
235 rtnl_unlock(); 225 rtnl_unlock();
236} 226}
237 227