diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2008-09-10 18:01:51 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-09-15 16:48:20 -0400 |
commit | 5bc75728fd43bb15b46f16ef465bcf9d487393cf (patch) | |
tree | 5732adee3965970390bf7953d214c757bbdba2a2 /net/mac80211/main.c | |
parent | b7413430d4d2a6168e68231d9f93763047b6d60c (diff) |
mac80211: fix scan vs. interface removal race
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>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/main.c')
-rw-r--r-- | net/mac80211/main.c | 33 |
1 files changed, 25 insertions, 8 deletions
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index ebdec7106d63..4bfac4b41c51 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -564,14 +564,6 @@ static int ieee80211_stop(struct net_device *dev) | |||
564 | synchronize_rcu(); | 564 | synchronize_rcu(); |
565 | skb_queue_purge(&sdata->u.sta.skb_queue); | 565 | skb_queue_purge(&sdata->u.sta.skb_queue); |
566 | 566 | ||
567 | if (local->scan_sdata == sdata) { | ||
568 | if (!local->ops->hw_scan) { | ||
569 | local->sta_sw_scanning = 0; | ||
570 | cancel_delayed_work(&local->scan_work); | ||
571 | } else | ||
572 | local->sta_hw_scanning = 0; | ||
573 | } | ||
574 | |||
575 | sdata->u.sta.flags &= ~IEEE80211_STA_PRIVACY_INVOKED; | 567 | sdata->u.sta.flags &= ~IEEE80211_STA_PRIVACY_INVOKED; |
576 | kfree(sdata->u.sta.extra_ie); | 568 | kfree(sdata->u.sta.extra_ie); |
577 | sdata->u.sta.extra_ie = NULL; | 569 | sdata->u.sta.extra_ie = NULL; |
@@ -585,6 +577,31 @@ static int ieee80211_stop(struct net_device *dev) | |||
585 | } | 577 | } |
586 | /* fall through */ | 578 | /* fall through */ |
587 | default: | 579 | default: |
580 | if (local->scan_sdata == sdata) { | ||
581 | if (!local->ops->hw_scan) | ||
582 | cancel_delayed_work_sync(&local->scan_work); | ||
583 | /* | ||
584 | * The software scan can no longer run now, so we can | ||
585 | * clear out the scan_sdata reference. However, the | ||
586 | * hardware scan may still be running. The complete | ||
587 | * function must be prepared to handle a NULL value. | ||
588 | */ | ||
589 | local->scan_sdata = NULL; | ||
590 | /* | ||
591 | * The memory barrier guarantees that another CPU | ||
592 | * that is hardware-scanning will now see the fact | ||
593 | * that this interface is gone. | ||
594 | */ | ||
595 | smp_mb(); | ||
596 | /* | ||
597 | * If software scanning, complete the scan but since | ||
598 | * the scan_sdata is NULL already don't send out a | ||
599 | * scan event to userspace -- the scan is incomplete. | ||
600 | */ | ||
601 | if (local->sta_sw_scanning) | ||
602 | ieee80211_scan_completed(&local->hw); | ||
603 | } | ||
604 | |||
588 | conf.vif = &sdata->vif; | 605 | conf.vif = &sdata->vif; |
589 | conf.type = sdata->vif.type; | 606 | conf.type = sdata->vif.type; |
590 | conf.mac_addr = dev->dev_addr; | 607 | conf.mac_addr = dev->dev_addr; |