aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/cfg.c
diff options
context:
space:
mode:
authorMichal Kazior <michal.kazior@tieto.com>2014-04-09 09:29:32 -0400
committerJohannes Berg <johannes.berg@intel.com>2014-04-25 11:08:30 -0400
commit2b32713d72c093889fe20642f6a8bc42083267d2 (patch)
treed5773d77a88b306fe4d8c48cc2173c1a49483dcb /net/mac80211/cfg.c
parent1f0d54cdcf822894cebebaa6cdc4e838c32bfb08 (diff)
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: Michal Kazior <michal.kazior@tieto.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/cfg.c')
-rw-r--r--net/mac80211/cfg.c17
1 files changed, 9 insertions, 8 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index aa39381ca46d..9620d4fba0d1 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -3225,7 +3225,7 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
3225{ 3225{
3226 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); 3226 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
3227 struct ieee80211_local *local = sdata->local; 3227 struct ieee80211_local *local = sdata->local;
3228 struct ieee80211_chanctx_conf *chanctx_conf; 3228 struct ieee80211_chanctx_conf *conf;
3229 struct ieee80211_chanctx *chanctx; 3229 struct ieee80211_chanctx *chanctx;
3230 int err, num_chanctx, changed = 0; 3230 int err, num_chanctx, changed = 0;
3231 3231
@@ -3241,23 +3241,24 @@ int ieee80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
3241 &sdata->vif.bss_conf.chandef)) 3241 &sdata->vif.bss_conf.chandef))
3242 return -EINVAL; 3242 return -EINVAL;
3243 3243
3244 rcu_read_lock(); 3244 mutex_lock(&local->chanctx_mtx);
3245 chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); 3245 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
3246 if (!chanctx_conf) { 3246 lockdep_is_held(&local->chanctx_mtx));
3247 rcu_read_unlock(); 3247 if (!conf) {
3248 mutex_unlock(&local->chanctx_mtx);
3248 return -EBUSY; 3249 return -EBUSY;
3249 } 3250 }
3250 3251
3251 /* don't handle for multi-VIF cases */ 3252 /* don't handle for multi-VIF cases */
3252 chanctx = container_of(chanctx_conf, struct ieee80211_chanctx, conf); 3253 chanctx = container_of(conf, struct ieee80211_chanctx, conf);
3253 if (chanctx->refcount > 1) { 3254 if (chanctx->refcount > 1) {
3254 rcu_read_unlock(); 3255 mutex_unlock(&local->chanctx_mtx);
3255 return -EBUSY; 3256 return -EBUSY;
3256 } 3257 }
3257 num_chanctx = 0; 3258 num_chanctx = 0;
3258 list_for_each_entry_rcu(chanctx, &local->chanctx_list, list) 3259 list_for_each_entry_rcu(chanctx, &local->chanctx_list, list)
3259 num_chanctx++; 3260 num_chanctx++;
3260 rcu_read_unlock(); 3261 mutex_unlock(&local->chanctx_mtx);
3261 3262
3262 if (num_chanctx > 1) 3263 if (num_chanctx > 1)
3263 return -EBUSY; 3264 return -EBUSY;