aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorReinette Chatre <reinette.chatre@intel.com>2010-05-13 17:49:44 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-05-21 14:40:00 -0400
commit073d5eab6fc85b6c278d507a5633b759a85dc878 (patch)
treef6679bcec13c8f0ef8f6f9398412542d4bf0b431 /drivers/net
parent22fe88d3d85850267ff4535b465794a5768f868a (diff)
iwlwifi: fix internal scan race
It is possible for internal scan to race against itself if the device is not returning the scan results from first requests. What happens in this case is the cleanup done during the abort of the first internal scan also cleans up part of the new scan, causing it to access memory it shouldn't. Here are details: * First internal scan is triggered and scan command sent to device. * After seven seconds there is no scan results so the watchdog timer triggers a scan abort. * The scan abort succeeds and a SCAN_COMPLETE_NOTIFICATION is received for failed scan. * During processing of SCAN_COMPLETE_NOTIFICATION we clear STATUS_SCANNING and queue the "scan_completed" work. ** At this time, since the problem that caused the internal scan in first place is still present, a new internal scan is triggered. The behavior at this point is a bit different between 2.6.34 and 2.6.35 since 2.6.35 has a lot of this synchronized. The rest of the race description will thus be generalized. ** As part of preparing for the scan "is_internal_short_scan" is set to true. * At this point the completion work for fist scan is run. As part of this there is some locking missing around the "is_internal_short_scan" variable and it is set to "false". ** Now the second scan runs and it considers itself a real (not internal0 scan and thus causes problems with wrong memory being accessed. The fix is twofold. * Since "is_internal_short_scan" should be protected by mutex, fix this in scan completion work so that changes to it can be serialized. * Do not queue a new internal scan if one is in progress. This fixes https://bugzilla.kernel.org/show_bug.cgi?id=15824 Signed-off-by: Reinette Chatre <reinette.chatre@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-scan.c21
1 files changed, 18 insertions, 3 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c
index 107e173112f6..5d3f51ff2f0d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-scan.c
+++ b/drivers/net/wireless/iwlwifi/iwl-scan.c
@@ -376,6 +376,11 @@ void iwl_bg_start_internal_scan(struct work_struct *work)
376 376
377 mutex_lock(&priv->mutex); 377 mutex_lock(&priv->mutex);
378 378
379 if (priv->is_internal_short_scan == true) {
380 IWL_DEBUG_SCAN(priv, "Internal scan already in progress\n");
381 goto unlock;
382 }
383
379 if (!iwl_is_ready_rf(priv)) { 384 if (!iwl_is_ready_rf(priv)) {
380 IWL_DEBUG_SCAN(priv, "not ready or exit pending\n"); 385 IWL_DEBUG_SCAN(priv, "not ready or exit pending\n");
381 goto unlock; 386 goto unlock;
@@ -497,17 +502,27 @@ void iwl_bg_scan_completed(struct work_struct *work)
497{ 502{
498 struct iwl_priv *priv = 503 struct iwl_priv *priv =
499 container_of(work, struct iwl_priv, scan_completed); 504 container_of(work, struct iwl_priv, scan_completed);
505 bool internal = false;
500 506
501 IWL_DEBUG_SCAN(priv, "SCAN complete scan\n"); 507 IWL_DEBUG_SCAN(priv, "SCAN complete scan\n");
502 508
503 cancel_delayed_work(&priv->scan_check); 509 cancel_delayed_work(&priv->scan_check);
504 510
505 if (!priv->is_internal_short_scan) 511 mutex_lock(&priv->mutex);
506 ieee80211_scan_completed(priv->hw, false); 512 if (priv->is_internal_short_scan) {
507 else {
508 priv->is_internal_short_scan = false; 513 priv->is_internal_short_scan = false;
509 IWL_DEBUG_SCAN(priv, "internal short scan completed\n"); 514 IWL_DEBUG_SCAN(priv, "internal short scan completed\n");
515 internal = true;
510 } 516 }
517 mutex_unlock(&priv->mutex);
518
519 /*
520 * Do not hold mutex here since this will cause mac80211 to call
521 * into driver again into functions that will attempt to take
522 * mutex.
523 */
524 if (!internal)
525 ieee80211_scan_completed(priv->hw, false);
511 526
512 if (test_bit(STATUS_EXIT_PENDING, &priv->status)) 527 if (test_bit(STATUS_EXIT_PENDING, &priv->status))
513 return; 528 return;