diff options
author | Michal Kazior <michal.kazior@tieto.com> | 2014-04-09 09:29:32 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2014-04-25 11:08:30 -0400 |
commit | 2b32713d72c093889fe20642f6a8bc42083267d2 (patch) | |
tree | d5773d77a88b306fe4d8c48cc2173c1a49483dcb /net/mac80211/cfg.c | |
parent | 1f0d54cdcf822894cebebaa6cdc4e838c32bfb08 (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.c | 17 |
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; |