diff options
author | Johannes Berg <johannes.berg@intel.com> | 2017-06-10 06:52:43 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2017-06-13 04:24:33 -0400 |
commit | c87905bec5dad66aa6bb43d11502cafdb33e07db (patch) | |
tree | 1b3c0ab10eeae62cc057c93b3f6189a90555eeb3 | |
parent | 44f6d42cbd6e4c1d4d25f19502dd5f27aedf89d4 (diff) |
mac80211: set bss_info data before configuring the channel
When mac80211 changes the channel, it also calls into the driver's
bss_info_changed() callback, e.g. with BSS_CHANGED_IDLE. The driver
may, like iwlwifi does, access more data from bss_info in that case
and iwlwifi accesses the basic_rates bitmap, but if changing from a
band with more (basic) rates to one with fewer, an out-of-bounds
access of the rate array may result.
While we can't avoid having invalid data at some point in time, we
can avoid having it while we call the driver - so set up all the
data before configuring the channel, and then apply it afterwards.
This fixes https://bugzilla.kernel.org/show_bug.cgi?id=195677
Reported-by: Johannes Hirte <johannes.hirte@datenkhaos.de>
Tested-by: Johannes Hirte <johannes.hirte@datenkhaos.de>
Debugged-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r-- | net/mac80211/mlme.c | 38 |
1 files changed, 28 insertions, 10 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 2f46db7d3afc..cc8e6ea1b27e 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -4392,15 +4392,19 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, | |||
4392 | return -ENOMEM; | 4392 | return -ENOMEM; |
4393 | } | 4393 | } |
4394 | 4394 | ||
4395 | if (new_sta || override) { | 4395 | /* |
4396 | err = ieee80211_prep_channel(sdata, cbss); | 4396 | * Set up the information for the new channel before setting the |
4397 | if (err) { | 4397 | * new channel. We can't - completely race-free - change the basic |
4398 | if (new_sta) | 4398 | * rates bitmap and the channel (sband) that it refers to, but if |
4399 | sta_info_free(local, new_sta); | 4399 | * we set it up before we at least avoid calling into the driver's |
4400 | return -EINVAL; | 4400 | * bss_info_changed() method with invalid information (since we do |
4401 | } | 4401 | * call that from changing the channel - only for IDLE and perhaps |
4402 | } | 4402 | * some others, but ...). |
4403 | 4403 | * | |
4404 | * So to avoid that, just set up all the new information before the | ||
4405 | * channel, but tell the driver to apply it only afterwards, since | ||
4406 | * it might need the new channel for that. | ||
4407 | */ | ||
4404 | if (new_sta) { | 4408 | if (new_sta) { |
4405 | u32 rates = 0, basic_rates = 0; | 4409 | u32 rates = 0, basic_rates = 0; |
4406 | bool have_higher_than_11mbit; | 4410 | bool have_higher_than_11mbit; |
@@ -4471,8 +4475,22 @@ static int ieee80211_prep_connection(struct ieee80211_sub_if_data *sdata, | |||
4471 | sdata->vif.bss_conf.sync_dtim_count = 0; | 4475 | sdata->vif.bss_conf.sync_dtim_count = 0; |
4472 | } | 4476 | } |
4473 | rcu_read_unlock(); | 4477 | rcu_read_unlock(); |
4478 | } | ||
4474 | 4479 | ||
4475 | /* tell driver about BSSID, basic rates and timing */ | 4480 | if (new_sta || override) { |
4481 | err = ieee80211_prep_channel(sdata, cbss); | ||
4482 | if (err) { | ||
4483 | if (new_sta) | ||
4484 | sta_info_free(local, new_sta); | ||
4485 | return -EINVAL; | ||
4486 | } | ||
4487 | } | ||
4488 | |||
4489 | if (new_sta) { | ||
4490 | /* | ||
4491 | * tell driver about BSSID, basic rates and timing | ||
4492 | * this was set up above, before setting the channel | ||
4493 | */ | ||
4476 | ieee80211_bss_info_change_notify(sdata, | 4494 | ieee80211_bss_info_change_notify(sdata, |
4477 | BSS_CHANGED_BSSID | BSS_CHANGED_BASIC_RATES | | 4495 | BSS_CHANGED_BSSID | BSS_CHANGED_BASIC_RATES | |
4478 | BSS_CHANGED_BEACON_INT); | 4496 | BSS_CHANGED_BEACON_INT); |