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

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
...@@ -3225,7 +3225,7 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, ...@@ -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_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_chanctx_conf *conf;
struct ieee80211_chanctx *chanctx; struct ieee80211_chanctx *chanctx;
int err, num_chanctx, changed = 0; int err, num_chanctx, changed = 0;
...@@ -3241,23 +3241,24 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, ...@@ -3241,23 +3241,24 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
&sdata->vif.bss_conf.chandef)) &sdata->vif.bss_conf.chandef))
return -EINVAL; return -EINVAL;
rcu_read_lock(); mutex_lock(&local->chanctx_mtx);
chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
if (!chanctx_conf) { lockdep_is_held(&local->chanctx_mtx));
rcu_read_unlock(); if (!conf) {
mutex_unlock(&local->chanctx_mtx);
return -EBUSY; return -EBUSY;
} }
/* don't handle for multi-VIF cases */ /* 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) { if (chanctx->refcount > 1) {
rcu_read_unlock(); mutex_unlock(&local->chanctx_mtx);
return -EBUSY; return -EBUSY;
} }
num_chanctx = 0; num_chanctx = 0;
list_for_each_entry_rcu(chanctx, &local->chanctx_list, list) list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
num_chanctx++; num_chanctx++;
rcu_read_unlock(); mutex_unlock(&local->chanctx_mtx);
if (num_chanctx > 1) if (num_chanctx > 1)
return -EBUSY; return -EBUSY;
......
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