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

Commit 2b32713d authored by Michal Kazior's avatar Michal Kazior Committed by Johannes Berg
Browse files

mac80211: fix racy usage of chanctx->refcount



Channel context refcount is protected by
chanctx_mtx. Accessing the value without holding
the mutex is racy. RCU section didn't guarantee
anything here.

Theoretically ieee80211_channel_switch() could
fail to see refcount change and read "1" instead
of, e.g. "2". This means mac80211 could accept CSA
even though it shouldn't have.

Signed-off-by: default avatarMichal Kazior <michal.kazior@tieto.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 1f0d54cd
Loading
Loading
Loading
Loading
+9 −8
Original line number Diff line number Diff line
@@ -3225,7 +3225,7 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct ieee80211_local *local = sdata->local;
	struct ieee80211_chanctx_conf *chanctx_conf;
	struct ieee80211_chanctx_conf *conf;
	struct ieee80211_chanctx *chanctx;
	int err, num_chanctx, changed = 0;

@@ -3241,23 +3241,24 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
				       &sdata->vif.bss_conf.chandef))
		return -EINVAL;

	rcu_read_lock();
	chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
	if (!chanctx_conf) {
		rcu_read_unlock();
	mutex_lock(&local->chanctx_mtx);
	conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
					 lockdep_is_held(&local->chanctx_mtx));
	if (!conf) {
		mutex_unlock(&local->chanctx_mtx);
		return -EBUSY;
	}

	/* don't handle for multi-VIF cases */
	chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf);
	chanctx = container_of(conf, struct ieee80211_chanctx, conf);
	if (chanctx->refcount > 1) {
		rcu_read_unlock();
		mutex_unlock(&local->chanctx_mtx);
		return -EBUSY;
	}
	num_chanctx = 0;
	list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
		num_chanctx++;
	rcu_read_unlock();
	mutex_unlock(&local->chanctx_mtx);

	if (num_chanctx > 1)
		return -EBUSY;