Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -387,7 +387,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy,
		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, "SSID count: %d", request->n_ssids);
@@ -868,6 +868,9 @@ static int wil_cfg80211_set_default_key(struct wiphy *wiphy,
					u8 key_index, bool unicast,
					bool multicast)
{
	struct wil6210_priv *wil = wiphy_to_wil(wiphy);

	wil_dbg_misc(wil, "%s: entered\n", __func__);
	return 0;
}

@@ -903,9 +906,7 @@ static int wil_cancel_remain_on_channel(struct wiphy *wiphy,

	wil_dbg_misc(wil, "%s()\n", __func__);

	wil_p2p_cancel_listen(wil, cookie);

	return 0;
	return wil_p2p_cancel_listen(wil, cookie);
}

/**
+1 −1
Original line number Diff line number Diff line
@@ -984,7 +984,7 @@ int __wil_down(struct wil6210_priv *wil)
	}
	wil_enable_irq(wil);

	wil_p2p_stop_discovery(wil);
	(void)wil_p2p_stop_discovery(wil);

	if (wil->scan_request) {
		wil_dbg_misc(wil, "Abort scan_request 0x%p\n",
+45 −18
Original line number Diff line number Diff line
@@ -156,26 +156,42 @@ int wil_p2p_listen(struct wil6210_priv *wil, unsigned int duration,
	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);

	wil_p2p_stop_discovery(wil);
	mutex_unlock(&wil->mutex);

	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,11 +209,15 @@ 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);

	if (started) {
		mutex_lock(&wil->p2p_wdev_mutex);
		cfg80211_remain_on_channel_expired(wil->radio_wdev,
						   p2p->cookie,
@@ -204,6 +225,7 @@ void wil_p2p_listen_expired(struct work_struct *work)
						   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);

	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);
	}
}
+2 −2
Original line number Diff line number Diff line
@@ -775,8 +775,8 @@ int wil_p2p_search(struct wil6210_priv *wil,
		   struct cfg80211_scan_request *request);
int wil_p2p_listen(struct wil6210_priv *wil, unsigned int duration,
		   struct ieee80211_channel *chan, u64 *cookie);
void wil_p2p_stop_discovery(struct wil6210_priv *wil);
void wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie);
u8 wil_p2p_stop_discovery(struct wil6210_priv *wil);
int wil_p2p_cancel_listen(struct wil6210_priv *wil, u64 cookie);
void wil_p2p_listen_expired(struct work_struct *work);
void wil_p2p_search_expired(struct work_struct *work);