Commit 280ab987 authored by Lior David's avatar Lior David Committed by Kalle Valo

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: default avatarLior David <qca_liord@qca.qualcomm.com>
Signed-off-by: default avatarMaya Erez <qca_merez@qca.qualcomm.com>
Signed-off-by: default avatarKalle Valo <kvalo@qca.qualcomm.com>
parent 4332cac1
...@@ -387,7 +387,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, ...@@ -387,7 +387,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
return rc; return rc;
} }
wil_p2p_stop_discovery(wil); (void)wil_p2p_stop_discovery(wil);
wil_dbg_misc(wil, "Start scan_request 0x%p\n", request); wil_dbg_misc(wil, "Start scan_request 0x%p\n", request);
wil_dbg_misc(wil, "SSID count: %d", request->n_ssids); wil_dbg_misc(wil, "SSID count: %d", request->n_ssids);
...@@ -868,6 +868,9 @@ static int wil_cfg80211_set_default_key(struct wiphy *wiphy, ...@@ -868,6 +868,9 @@ static int wil_cfg80211_set_default_key(struct wiphy *wiphy,
u8 key_index, bool unicast, u8 key_index, bool unicast,
bool multicast) bool multicast)
{ {
struct wil6210_priv *wil = wiphy_to_wil(wiphy);
wil_dbg_misc(wil, "%s: entered\n", __func__);
return 0; return 0;
} }
...@@ -903,9 +906,7 @@ static int wil_cancel_remain_on_channel(struct wiphy *wiphy, ...@@ -903,9 +906,7 @@ static int wil_cancel_remain_on_channel(struct wiphy *wiphy,
wil_dbg_misc(wil, "%s()\n", __func__); wil_dbg_misc(wil, "%s()\n", __func__);
wil_p2p_cancel_listen(wil, cookie); return wil_p2p_cancel_listen(wil, cookie);
return 0;
} }
/** /**
......
...@@ -984,7 +984,7 @@ int __wil_down(struct wil6210_priv *wil) ...@@ -984,7 +984,7 @@ int __wil_down(struct wil6210_priv *wil)
} }
wil_enable_irq(wil); wil_enable_irq(wil);
wil_p2p_stop_discovery(wil); (void)wil_p2p_stop_discovery(wil);
if (wil->scan_request) { if (wil->scan_request) {
wil_dbg_misc(wil, "Abort scan_request 0x%p\n", wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
......
...@@ -156,26 +156,42 @@ int wil_p2p_listen(struct wil6210_priv *wil, unsigned int duration, ...@@ -156,26 +156,42 @@ int wil_p2p_listen(struct wil6210_priv *wil, unsigned int duration,
return rc; 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; struct wil_p2p_info *p2p = &wil->p2p;
u8 started = p2p->discovery_started;
if (p2p->discovery_started) { if (p2p->discovery_started) {
del_timer_sync(&p2p->discovery_timer); del_timer_sync(&p2p->discovery_timer);
p2p->discovery_started = 0; p2p->discovery_started = 0;
wmi_stop_discovery(wil); 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; 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", wil_info(wil, "%s: Cookie mismatch: 0x%016llx vs. 0x%016llx\n",
__func__, p2p->cookie, cookie); __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); mutex_lock(&wil->p2p_wdev_mutex);
cfg80211_remain_on_channel_expired(wil->radio_wdev, cfg80211_remain_on_channel_expired(wil->radio_wdev,
...@@ -184,6 +200,7 @@ void wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie) ...@@ -184,6 +200,7 @@ void wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie)
GFP_KERNEL); GFP_KERNEL);
wil->radio_wdev = wil->wdev; wil->radio_wdev = wil->wdev;
mutex_unlock(&wil->p2p_wdev_mutex); mutex_unlock(&wil->p2p_wdev_mutex);
return 0;
} }
void wil_p2p_listen_expired(struct work_struct *work) void wil_p2p_listen_expired(struct work_struct *work)
...@@ -192,18 +209,23 @@ 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 wil_p2p_info, discovery_expired_work);
struct wil6210_priv *wil = container_of(p2p, struct wil6210_priv *wil = container_of(p2p,
struct wil6210_priv, p2p); struct wil6210_priv, p2p);
u8 started;
wil_dbg_misc(wil, "%s()\n", __func__); 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); if (started) {
cfg80211_remain_on_channel_expired(wil->radio_wdev, mutex_lock(&wil->p2p_wdev_mutex);
p2p->cookie, cfg80211_remain_on_channel_expired(wil->radio_wdev,
&p2p->listen_chan, p2p->cookie,
GFP_KERNEL); &p2p->listen_chan,
wil->radio_wdev = wil->wdev; GFP_KERNEL);
mutex_unlock(&wil->p2p_wdev_mutex); wil->radio_wdev = wil->wdev;
mutex_unlock(&wil->p2p_wdev_mutex);
}
} }
...@@ -213,14 +235,19 @@ void wil_p2p_search_expired(struct work_struct *work) ...@@ -213,14 +235,19 @@ void wil_p2p_search_expired(struct work_struct *work)
struct wil_p2p_info, discovery_expired_work); struct wil_p2p_info, discovery_expired_work);
struct wil6210_priv *wil = container_of(p2p, struct wil6210_priv *wil = container_of(p2p,
struct wil6210_priv, p2p); struct wil6210_priv, p2p);
u8 started;
wil_dbg_misc(wil, "%s()\n", __func__); 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); if (started) {
cfg80211_scan_done(wil->scan_request, 0); mutex_lock(&wil->p2p_wdev_mutex);
wil->scan_request = NULL; cfg80211_scan_done(wil->scan_request, 0);
wil->radio_wdev = wil->wdev; wil->scan_request = NULL;
mutex_unlock(&wil->p2p_wdev_mutex); wil->radio_wdev = wil->wdev;
mutex_unlock(&wil->p2p_wdev_mutex);
}
} }
...@@ -775,8 +775,8 @@ int wil_p2p_search(struct wil6210_priv *wil, ...@@ -775,8 +775,8 @@ int wil_p2p_search(struct wil6210_priv *wil,
struct cfg80211_scan_request *request); struct cfg80211_scan_request *request);
int wil_p2p_listen(struct wil6210_priv *wil, unsigned int duration, int wil_p2p_listen(struct wil6210_priv *wil, unsigned int duration,
struct ieee80211_channel *chan, u64 *cookie); struct ieee80211_channel *chan, u64 *cookie);
void wil_p2p_stop_discovery(struct wil6210_priv *wil); u8 wil_p2p_stop_discovery(struct wil6210_priv *wil);
void wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie); int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie);
void wil_p2p_listen_expired(struct work_struct *work); void wil_p2p_listen_expired(struct work_struct *work);
void wil_p2p_search_expired(struct work_struct *work); void wil_p2p_search_expired(struct work_struct *work);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment