summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/ath/wil6210/p2p.c
diff options
context:
space:
mode:
authorLior David <qca_liord@qca.qualcomm.com>2016-03-01 20:18:14 +0300
committerKalle Valo <kvalo@qca.qualcomm.com>2016-03-07 12:43:20 +0300
commit280ab987ef21d1c196acb3af4663a99f94d9da00 (patch)
treeb853e200d91f4acaab7d09b00aa2b628da86aaa2 /drivers/net/wireless/ath/wil6210/p2p.c
parent4332cac17b5c0cb80d8b99fda33a0faad3238b0e (diff)
downloadlinux-280ab987ef21d1c196acb3af4663a99f94d9da00.tar.xz
wil6210: fix race conditions in p2p listen and search
Fix 2 race conditions found during test runs of P2P discovery: 1. Because wil_p2p_cancel_listen was not protected, user space could start a new P2P listen/search before wmi_stop_discovery completed. This caused a crash in the firmware. 2. In P2P listen, when listen timer expires and user space calls cancel_remain_on_channel at the same time, code could send the cfg80211_remain_on_channel_expired notification twice. Added protections with wil->mutex to several places that call wmi_stop_discovery. Signed-off-by: Lior David <qca_liord@qca.qualcomm.com> Signed-off-by: Maya Erez <qca_merez@qca.qualcomm.com> Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
Diffstat (limited to 'drivers/net/wireless/ath/wil6210/p2p.c')
-rw-r--r--drivers/net/wireless/ath/wil6210/p2p.c63
1 files changed, 45 insertions, 18 deletions
diff --git a/drivers/net/wireless/ath/wil6210/p2p.c b/drivers/net/wireless/ath/wil6210/p2p.c
index d223648076a0..2c1b8958180e 100644
--- a/drivers/net/wireless/ath/wil6210/p2p.c
+++ b/drivers/net/wireless/ath/wil6210/p2p.c
@@ -156,26 +156,42 @@ out:
return rc;
}
-void wil_p2p_stop_discovery(struct wil6210_priv *wil)
+u8 wil_p2p_stop_discovery(struct wil6210_priv *wil)
{
struct wil_p2p_info *p2p = &wil->p2p;
+ u8 started = p2p->discovery_started;
if (p2p->discovery_started) {
del_timer_sync(&p2p->discovery_timer);
p2p->discovery_started = 0;
wmi_stop_discovery(wil);
}
+
+ return started;
}
-void wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie)
+int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie)
{
struct wil_p2p_info *p2p = &wil->p2p;
+ u8 started;
+
+ mutex_lock(&wil->mutex);
- if (cookie != p2p->cookie)
+ if (cookie != p2p->cookie) {
wil_info(wil, "%s: Cookie mismatch: 0x%016llx vs. 0x%016llx\n",
__func__, p2p->cookie, cookie);
+ mutex_unlock(&wil->mutex);
+ return -ENOENT;
+ }
+
+ started = wil_p2p_stop_discovery(wil);
+
+ mutex_unlock(&wil->mutex);
- wil_p2p_stop_discovery(wil);
+ if (!started) {
+ wil_err(wil, "%s: listen not started\n", __func__);
+ return -ENOENT;
+ }
mutex_lock(&wil->p2p_wdev_mutex);
cfg80211_remain_on_channel_expired(wil->radio_wdev,
@@ -184,6 +200,7 @@ void wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie)
GFP_KERNEL);
wil->radio_wdev = wil->wdev;
mutex_unlock(&wil->p2p_wdev_mutex);
+ return 0;
}
void wil_p2p_listen_expired(struct work_struct *work)
@@ -192,18 +209,23 @@ void wil_p2p_listen_expired(struct work_struct *work)
struct wil_p2p_info, discovery_expired_work);
struct wil6210_priv *wil = container_of(p2p,
struct wil6210_priv, p2p);
+ u8 started;
wil_dbg_misc(wil, "%s()\n", __func__);
- wil_p2p_stop_discovery(wil);
+ mutex_lock(&wil->mutex);
+ started = wil_p2p_stop_discovery(wil);
+ mutex_unlock(&wil->mutex);
- mutex_lock(&wil->p2p_wdev_mutex);
- cfg80211_remain_on_channel_expired(wil->radio_wdev,
- p2p->cookie,
- &p2p->listen_chan,
- GFP_KERNEL);
- wil->radio_wdev = wil->wdev;
- mutex_unlock(&wil->p2p_wdev_mutex);
+ if (started) {
+ mutex_lock(&wil->p2p_wdev_mutex);
+ cfg80211_remain_on_channel_expired(wil->radio_wdev,
+ p2p->cookie,
+ &p2p->listen_chan,
+ GFP_KERNEL);
+ wil->radio_wdev = wil->wdev;
+ mutex_unlock(&wil->p2p_wdev_mutex);
+ }
}
@@ -213,14 +235,19 @@ void wil_p2p_search_expired(struct work_struct *work)
struct wil_p2p_info, discovery_expired_work);
struct wil6210_priv *wil = container_of(p2p,
struct wil6210_priv, p2p);
+ u8 started;
wil_dbg_misc(wil, "%s()\n", __func__);
- wil_p2p_stop_discovery(wil);
+ mutex_lock(&wil->mutex);
+ started = wil_p2p_stop_discovery(wil);
+ mutex_unlock(&wil->mutex);
- mutex_lock(&wil->p2p_wdev_mutex);
- cfg80211_scan_done(wil->scan_request, 0);
- wil->scan_request = NULL;
- wil->radio_wdev = wil->wdev;
- mutex_unlock(&wil->p2p_wdev_mutex);
+ if (started) {
+ mutex_lock(&wil->p2p_wdev_mutex);
+ cfg80211_scan_done(wil->scan_request, 0);
+ wil->scan_request = NULL;
+ wil->radio_wdev = wil->wdev;
+ mutex_unlock(&wil->p2p_wdev_mutex);
+ }
}