aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2017-06-10 06:52:43 -0400
committerJohannes Berg <johannes.berg@intel.com>2017-06-13 04:24:33 -0400
commitc87905bec5dad66aa6bb43d11502cafdb33e07db (patch)
tree1b3c0ab10eeae62cc057c93b3f6189a90555eeb3
parent44f6d42cbd6e4c1d4d25f19502dd5f27aedf89d4 (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.c38
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);