linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Johannes Berg <johannes@sipsolutions.net>
To: John Linville <linville@tuxdriver.com>
Cc: linux-wireless@vger.kernel.org
Subject: [PATCH 06/18] mac80211: fix scan vs. interface removal race
Date: Thu, 11 Sep 2008 00:01:51 +0200	[thread overview]
Message-ID: <20080910220414.322392000@sipsolutions.net> (raw)
In-Reply-To: 20080910220145.707263000@sipsolutions.net

When we remove an interface, we can currently end up having
a pointer to it left in local->scan_sdata after it has been
set down, and then with a hardware scan the scan completion
can try to access it which is a bug. Alternatively, a scan
that started as a hardware scan may terminate as though it
was a software scan, if the timing is just right.

On SMP systems, software scan also has a similar problem,
just canceling the delayed work and setting a flag isn't
enough since it may be running concurrently; in this case
we would also never restore state of other interfaces.

This patch hopefully fixes the problems by always invoking
ieee80211_scan_completed or requiring it to be invoked by
the driver, I suspect the drivers that have ->hw_scan() are
buggy. The bug will not manifest itself unless you remove
the interface while hw-scanning which will also turn off
the hw, and then add a new interface which will be unusable
until you scan once.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 include/net/mac80211.h |    4 +++-
 net/mac80211/main.c    |   33 +++++++++++++++++++++++++--------
 net/mac80211/mlme.c    |    2 +-
 net/mac80211/scan.c    |   38 +++++++++++++++++++++++++++-----------
 4 files changed, 56 insertions(+), 21 deletions(-)

--- everything.orig/net/mac80211/main.c	2008-09-10 23:57:57.000000000 +0200
+++ everything/net/mac80211/main.c	2008-09-10 23:57:58.000000000 +0200
@@ -564,14 +564,6 @@ static int ieee80211_stop(struct net_dev
 		synchronize_rcu();
 		skb_queue_purge(&sdata->u.sta.skb_queue);
 
-		if (local->scan_sdata == sdata) {
-			if (!local->ops->hw_scan) {
-				local->sta_sw_scanning = 0;
-				cancel_delayed_work(&local->scan_work);
-			} else
-				local->sta_hw_scanning = 0;
-		}
-
 		sdata->u.sta.flags &= ~IEEE80211_STA_PRIVACY_INVOKED;
 		kfree(sdata->u.sta.extra_ie);
 		sdata->u.sta.extra_ie = NULL;
@@ -585,6 +577,31 @@ static int ieee80211_stop(struct net_dev
 		}
 		/* fall through */
 	default:
+		if (local->scan_sdata == sdata) {
+			if (!local->ops->hw_scan)
+				cancel_delayed_work_sync(&local->scan_work);
+			/*
+			 * The software scan can no longer run now, so we can
+			 * clear out the scan_sdata reference. However, the
+			 * hardware scan may still be running. The complete
+			 * function must be prepared to handle a NULL value.
+			 */
+			local->scan_sdata = NULL;
+			/*
+			 * The memory barrier guarantees that another CPU
+			 * that is hardware-scanning will now see the fact
+			 * that this interface is gone.
+			 */
+			smp_mb();
+			/*
+			 * If software scanning, complete the scan but since
+			 * the scan_sdata is NULL already don't send out a
+			 * scan event to userspace -- the scan is incomplete.
+			 */
+			if (local->sta_sw_scanning)
+				ieee80211_scan_completed(&local->hw);
+		}
+
 		conf.vif = &sdata->vif;
 		conf.type = sdata->vif.type;
 		conf.mac_addr = dev->dev_addr;
--- everything.orig/net/mac80211/mlme.c	2008-09-10 23:57:55.000000000 +0200
+++ everything/net/mac80211/mlme.c	2008-09-10 23:57:58.000000000 +0200
@@ -2561,7 +2561,7 @@ void ieee80211_mlme_notify_scan_complete
 	struct ieee80211_sub_if_data *sdata = local->scan_sdata;
 	struct ieee80211_if_sta *ifsta;
 
-	if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
+	if (sdata && sdata->vif.type == IEEE80211_IF_TYPE_IBSS) {
 		ifsta = &sdata->u.sta;
 		if (!(ifsta->flags & IEEE80211_STA_BSSID_SET) ||
 		    (!(ifsta->state == IEEE80211_STA_MLME_IBSS_JOINED) &&
--- everything.orig/net/mac80211/scan.c	2008-09-10 23:57:55.000000000 +0200
+++ everything/net/mac80211/scan.c	2008-09-10 23:57:58.000000000 +0200
@@ -430,9 +430,20 @@ void ieee80211_scan_completed(struct iee
 	struct ieee80211_sub_if_data *sdata;
 	union iwreq_data wrqu;
 
+	if (WARN_ON(!local->sta_hw_scanning && !local->sta_sw_scanning))
+		return;
+
 	local->last_scan_completed = jiffies;
 	memset(&wrqu, 0, sizeof(wrqu));
-	wireless_send_event(local->scan_sdata->dev, SIOCGIWSCAN, &wrqu, NULL);
+
+	/*
+	 * local->scan_sdata could have been NULLed by the interface
+	 * down code in case we were scanning on an interface that is
+	 * being taken down.
+	 */
+	sdata = local->scan_sdata;
+	if (sdata)
+		wireless_send_event(sdata->dev, SIOCGIWSCAN, &wrqu, NULL);
 
 	if (local->sta_hw_scanning) {
 		local->sta_hw_scanning = 0;
@@ -491,7 +502,10 @@ void ieee80211_sta_scan_work(struct work
 	int skip;
 	unsigned long next_delay = 0;
 
-	if (!local->sta_sw_scanning)
+	/*
+	 * Avoid re-scheduling when the sdata is going away.
+	 */
+	if (!netif_running(sdata->dev))
 		return;
 
 	switch (local->scan_state) {
@@ -570,9 +584,8 @@ void ieee80211_sta_scan_work(struct work
 		break;
 	}
 
-	if (local->sta_sw_scanning)
-		queue_delayed_work(local->hw.workqueue, &local->scan_work,
-				   next_delay);
+	queue_delayed_work(local->hw.workqueue, &local->scan_work,
+			   next_delay);
 }
 
 
@@ -609,13 +622,16 @@ int ieee80211_sta_start_scan(struct ieee
 	}
 
 	if (local->ops->hw_scan) {
-		int rc = local->ops->hw_scan(local_to_hw(local),
-					     ssid, ssid_len);
-		if (!rc) {
-			local->sta_hw_scanning = 1;
-			local->scan_sdata = scan_sdata;
+		int rc;
+
+		local->sta_hw_scanning = 1;
+		rc = local->ops->hw_scan(local_to_hw(local), ssid, ssid_len);
+		if (rc) {
+			local->sta_hw_scanning = 0;
+			return rc;
 		}
-		return rc;
+		local->scan_sdata = scan_sdata;
+		return 0;
 	}
 
 	local->sta_sw_scanning = 1;
--- everything.orig/include/net/mac80211.h	2008-09-10 23:50:28.000000000 +0200
+++ everything/include/net/mac80211.h	2008-09-10 23:57:58.000000000 +0200
@@ -1122,7 +1122,9 @@ enum ieee80211_ampdu_mlme_action {
  * @hw_scan: Ask the hardware to service the scan request, no need to start
  *	the scan state machine in stack. The scan must honour the channel
  *	configuration done by the regulatory agent in the wiphy's registered
- *	bands.
+ *	bands. When the scan finishes, ieee80211_scan_completed() must be
+ *	called; note that it also must be called when the scan cannot finish
+ *	because the hardware is turned off! Anything else is a bug!
  *
  * @get_stats: return low-level statistics
  *

-- 


  parent reply	other threads:[~2008-09-10 22:09 UTC|newest]

Thread overview: 46+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-09-10 22:01 [PATCH 00/18] mac80211 cleanups and fixes Johannes Berg
2008-09-10 22:01 ` [PATCH 01/18] mac80211: move ieee80211_sta_expire Johannes Berg
2008-09-10 22:01 ` [PATCH 02/18] mac80211: move STA timer restart Johannes Berg
2008-09-10 22:01 ` [PATCH 03/18] mac80211: dont set REQ_RUN when scan finishes Johannes Berg
2008-09-10 22:01 ` [PATCH 04/18] mac80211: split off mesh handling entirely Johannes Berg
2008-09-10 22:01 ` [PATCH 05/18] mac80211: fix work race Johannes Berg
2008-09-10 22:01 ` Johannes Berg [this message]
2008-09-10 22:01 ` [PATCH 07/18] mac80211: reorder MLME code more Johannes Berg
2008-09-10 22:01 ` [PATCH 08/18] mac80211: move ieee80211_set_freq to utils Johannes Berg
2008-09-10 22:01 ` [PATCH 09/18] mac80211: make bridge_packets a virtual interface option Johannes Berg
2008-09-10 22:01 ` [PATCH 10/18] mac80211: clean up scan namespace Johannes Berg
2008-09-10 22:01 ` [PATCH 11/18] mac80211: clean up some comments Johannes Berg
2008-09-10 22:01 ` [PATCH 12/18] mac80211: inform driver of basic rateset Johannes Berg
2008-09-10 22:01 ` [PATCH 13/18] mac80211: use nl80211 interface types Johannes Berg
2008-09-10 22:01 ` [PATCH 14/18] mac80211: move regular interface handling Johannes Berg
2008-09-10 22:02 ` [PATCH 15/18] mac80211: warn on some invalid vlan operations Johannes Berg
2008-09-10 22:02 ` [PATCH 16/18] mac80211 hwsim: verify vif pointers Johannes Berg
2008-09-11  0:06   ` Luis R. Rodriguez
2008-09-11  0:09     ` Johannes Berg
2008-09-11  0:17       ` Luis R. Rodriguez
2008-09-11  0:16   ` [PATCH v2 " Johannes Berg
2008-09-10 22:02 ` [PATCH 17/18] mac80211: share STA information with driver Johannes Berg
2008-09-10 22:02 ` [PATCH 18/18] mac80211 hwsim: verify sta pointers Johannes Berg
2008-09-11  0:17   ` [PATCH v2 " Johannes Berg
2008-09-11 19:26     ` Johannes Berg
2008-09-11  0:03 ` [PATCH 19/18] mac80211: small rate control changes Johannes Berg
2008-09-11  0:22 ` [PATCH 20/18] mac80211: move last_txrate_idx into RC algorithms Johannes Berg
2008-09-11  0:45 ` [PATCH 21/18] mac80211: share sta->supp_rates Johannes Berg
2008-09-11  1:04 ` [PATCH 22/18] mac80211: move txrate_idx into RC algorithms Johannes Berg
2008-09-11  1:14 ` [PATCH 23/18] mac80211: share sta_info->ht_info Johannes Berg
2008-09-11  1:17 ` [PATCH 24/18] iwlwifi: don't access mac80211's AMPDU state machine Johannes Berg
2008-09-11  3:27 ` [PATCH 25/18] mac80211: pass AP vif pointer for VLANs Johannes Berg
2008-09-11  8:29 ` [PATCH 00/18] mac80211 cleanups and fixes Sujith
2008-09-11 13:22   ` Johannes Berg
2008-09-11 16:50     ` Luis R. Rodriguez
2008-09-11 16:53       ` Johannes Berg
2008-09-11 17:10         ` Luis R. Rodriguez
2008-09-11 17:13           ` Johannes Berg
2008-09-11 17:31             ` Luis R. Rodriguez
2008-09-11 17:33         ` Luis R. Rodriguez
2008-09-11 17:39           ` Johannes Berg
2008-09-11 17:42             ` Johannes Berg
2008-09-11 17:47               ` Johannes Berg
2008-09-11 17:54             ` Luis R. Rodriguez
2008-09-12  3:14     ` Sujith
2008-09-12  7:45       ` Johannes Berg

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20080910220414.322392000@sipsolutions.net \
    --to=johannes@sipsolutions.net \
    --cc=linux-wireless@vger.kernel.org \
    --cc=linville@tuxdriver.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).