aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/mlme.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-11-28 19:25:20 -0500
committerJohannes Berg <johannes.berg@intel.com>2012-11-30 07:42:20 -0500
commit9caf03640279e64d0ba36539b42daa1b43a49486 (patch)
treecb094a4a577f61421d1b402e16f0e68f151d5726 /net/mac80211/mlme.c
parentb9a9ada14aab17f08c1d9735601f1097cdcfc6de (diff)
cfg80211: fix BSS struct IE access races
When a BSS struct is updated, the IEs are currently overwritten or freed. This can lead to races if some other CPU is accessing the BSS struct and using the IEs concurrently. Fix this by always allocating the IEs in a new struct that holds the data and length and protecting access to this new struct with RCU. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/mlme.c')
-rw-r--r--net/mac80211/mlme.c80
1 files changed, 49 insertions, 31 deletions
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 35d8cffc973a..481d5035b397 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1382,19 +1382,26 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
1382 sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE; 1382 sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;
1383 1383
1384 if (sdata->vif.p2p) { 1384 if (sdata->vif.p2p) {
1385 u8 noa[2]; 1385 const struct cfg80211_bss_ies *ies;
1386 int ret;
1387 1386
1388 ret = cfg80211_get_p2p_attr(cbss->information_elements, 1387 rcu_read_lock();
1389 cbss->len_information_elements, 1388 ies = rcu_dereference(cbss->ies);
1390 IEEE80211_P2P_ATTR_ABSENCE_NOTICE, 1389 if (ies) {
1391 noa, sizeof(noa)); 1390 u8 noa[2];
1392 if (ret >= 2) { 1391 int ret;
1393 bss_conf->p2p_oppps = noa[1] & 0x80; 1392
1394 bss_conf->p2p_ctwindow = noa[1] & 0x7f; 1393 ret = cfg80211_get_p2p_attr(
1395 bss_info_changed |= BSS_CHANGED_P2P_PS; 1394 ies->data, ies->len,
1396 sdata->u.mgd.p2p_noa_index = noa[0]; 1395 IEEE80211_P2P_ATTR_ABSENCE_NOTICE,
1396 noa, sizeof(noa));
1397 if (ret >= 2) {
1398 bss_conf->p2p_oppps = noa[1] & 0x80;
1399 bss_conf->p2p_ctwindow = noa[1] & 0x7f;
1400 bss_info_changed |= BSS_CHANGED_P2P_PS;
1401 sdata->u.mgd.p2p_noa_index = noa[0];
1402 }
1397 } 1403 }
1404 rcu_read_unlock();
1398 } 1405 }
1399 1406
1400 /* just to be sure */ 1407 /* just to be sure */
@@ -1659,6 +1666,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
1659 } else { 1666 } else {
1660 int ssid_len; 1667 int ssid_len;
1661 1668
1669 rcu_read_lock();
1662 ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID); 1670 ssid = ieee80211_bss_get_ie(ifmgd->associated, WLAN_EID_SSID);
1663 if (WARN_ON_ONCE(ssid == NULL)) 1671 if (WARN_ON_ONCE(ssid == NULL))
1664 ssid_len = 0; 1672 ssid_len = 0;
@@ -1668,6 +1676,7 @@ static void ieee80211_mgd_probe_ap_send(struct ieee80211_sub_if_data *sdata)
1668 ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL, 1676 ieee80211_send_probe_req(sdata, dst, ssid + 2, ssid_len, NULL,
1669 0, (u32) -1, true, false, 1677 0, (u32) -1, true, false,
1670 ifmgd->associated->channel, false); 1678 ifmgd->associated->channel, false);
1679 rcu_read_unlock();
1671 } 1680 }
1672 1681
1673 ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms); 1682 ifmgd->probe_timeout = jiffies + msecs_to_jiffies(probe_wait_ms);
@@ -1763,6 +1772,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
1763 else 1772 else
1764 return NULL; 1773 return NULL;
1765 1774
1775 rcu_read_lock();
1766 ssid = ieee80211_bss_get_ie(cbss, WLAN_EID_SSID); 1776 ssid = ieee80211_bss_get_ie(cbss, WLAN_EID_SSID);
1767 if (WARN_ON_ONCE(ssid == NULL)) 1777 if (WARN_ON_ONCE(ssid == NULL))
1768 ssid_len = 0; 1778 ssid_len = 0;
@@ -1773,6 +1783,7 @@ struct sk_buff *ieee80211_ap_probereq_get(struct ieee80211_hw *hw,
1773 (u32) -1, cbss->channel, 1783 (u32) -1, cbss->channel,
1774 ssid + 2, ssid_len, 1784 ssid + 2, ssid_len,
1775 NULL, 0, true); 1785 NULL, 0, true);
1786 rcu_read_unlock();
1776 1787
1777 return skb; 1788 return skb;
1778} 1789}
@@ -2858,9 +2869,12 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
2858 auth_data->bss->bssid, auth_data->tries, 2869 auth_data->bss->bssid, auth_data->tries,
2859 IEEE80211_AUTH_MAX_TRIES); 2870 IEEE80211_AUTH_MAX_TRIES);
2860 2871
2872 rcu_read_lock();
2861 ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID); 2873 ssidie = ieee80211_bss_get_ie(auth_data->bss, WLAN_EID_SSID);
2862 if (!ssidie) 2874 if (!ssidie) {
2875 rcu_read_unlock();
2863 return -EINVAL; 2876 return -EINVAL;
2877 }
2864 /* 2878 /*
2865 * Direct probe is sent to broadcast address as some APs 2879 * Direct probe is sent to broadcast address as some APs
2866 * will not answer to direct packet in unassociated state. 2880 * will not answer to direct packet in unassociated state.
@@ -2868,6 +2882,7 @@ static int ieee80211_probe_auth(struct ieee80211_sub_if_data *sdata)
2868 ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1], 2882 ieee80211_send_probe_req(sdata, NULL, ssidie + 2, ssidie[1],
2869 NULL, 0, (u32) -1, true, false, 2883 NULL, 0, (u32) -1, true, false,
2870 auth_data->bss->channel, false); 2884 auth_data->bss->channel, false);
2885 rcu_read_unlock();
2871 } 2886 }
2872 2887
2873 auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT; 2888 auth_data->timeout = jiffies + IEEE80211_AUTH_TIMEOUT;
@@ -3404,9 +3419,7 @@ static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
3404 if (ifmgd->flags & IEEE80211_STA_DISABLE_HT) 3419 if (ifmgd->flags & IEEE80211_STA_DISABLE_HT)
3405 return chains; 3420 return chains;
3406 3421
3407 ht_cap_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, 3422 ht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_CAPABILITY);
3408 cbss->information_elements,
3409 cbss->len_information_elements);
3410 if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) { 3423 if (ht_cap_ie && ht_cap_ie[1] >= sizeof(*ht_cap)) {
3411 ht_cap = (void *)(ht_cap_ie + 2); 3424 ht_cap = (void *)(ht_cap_ie + 2);
3412 chains = ieee80211_mcs_to_chains(&ht_cap->mcs); 3425 chains = ieee80211_mcs_to_chains(&ht_cap->mcs);
@@ -3419,9 +3432,7 @@ static u8 ieee80211_ht_vht_rx_chains(struct ieee80211_sub_if_data *sdata,
3419 if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT) 3432 if (ifmgd->flags & IEEE80211_STA_DISABLE_VHT)
3420 return chains; 3433 return chains;
3421 3434
3422 vht_cap_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, 3435 vht_cap_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_VHT_CAPABILITY);
3423 cbss->information_elements,
3424 cbss->len_information_elements);
3425 if (vht_cap_ie && vht_cap_ie[1] >= sizeof(*vht_cap)) { 3436 if (vht_cap_ie && vht_cap_ie[1] >= sizeof(*vht_cap)) {
3426 u8 nss; 3437 u8 nss;
3427 u16 tx_mcs_map; 3438 u16 tx_mcs_map;
@@ -3457,13 +3468,13 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
3457 IEEE80211_STA_DISABLE_80P80MHZ | 3468 IEEE80211_STA_DISABLE_80P80MHZ |
3458 IEEE80211_STA_DISABLE_160MHZ); 3469 IEEE80211_STA_DISABLE_160MHZ);
3459 3470
3471 rcu_read_lock();
3472
3460 if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) && 3473 if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT) &&
3461 sband->ht_cap.ht_supported) { 3474 sband->ht_cap.ht_supported) {
3462 const u8 *ht_oper_ie; 3475 const u8 *ht_oper_ie;
3463 3476
3464 ht_oper_ie = cfg80211_find_ie(WLAN_EID_HT_OPERATION, 3477 ht_oper_ie = ieee80211_bss_get_ie(cbss, WLAN_EID_HT_OPERATION);
3465 cbss->information_elements,
3466 cbss->len_information_elements);
3467 if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper)) 3478 if (ht_oper_ie && ht_oper_ie[1] >= sizeof(*ht_oper))
3468 ht_oper = (void *)(ht_oper_ie + 2); 3479 ht_oper = (void *)(ht_oper_ie + 2);
3469 } 3480 }
@@ -3472,9 +3483,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
3472 sband->vht_cap.vht_supported) { 3483 sband->vht_cap.vht_supported) {
3473 const u8 *vht_oper_ie; 3484 const u8 *vht_oper_ie;
3474 3485
3475 vht_oper_ie = cfg80211_find_ie(WLAN_EID_VHT_OPERATION, 3486 vht_oper_ie = ieee80211_bss_get_ie(cbss,
3476 cbss->information_elements, 3487 WLAN_EID_VHT_OPERATION);
3477 cbss->len_information_elements);
3478 if (vht_oper_ie && vht_oper_ie[1] >= sizeof(*vht_oper)) 3488 if (vht_oper_ie && vht_oper_ie[1] >= sizeof(*vht_oper))
3479 vht_oper = (void *)(vht_oper_ie + 2); 3489 vht_oper = (void *)(vht_oper_ie + 2);
3480 if (vht_oper && !ht_oper) { 3490 if (vht_oper && !ht_oper) {
@@ -3494,6 +3504,8 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata,
3494 sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss), 3504 sdata->needed_rx_chains = min(ieee80211_ht_vht_rx_chains(sdata, cbss),
3495 local->rx_chains); 3505 local->rx_chains);
3496 3506
3507 rcu_read_unlock();
3508
3497 /* will change later if needed */ 3509 /* will change later if needed */
3498 sdata->smps_mode = IEEE80211_SMPS_OFF; 3510 sdata->smps_mode = IEEE80211_SMPS_OFF;
3499 3511
@@ -3734,14 +3746,21 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
3734 const u8 *ssidie, *ht_ie; 3746 const u8 *ssidie, *ht_ie;
3735 int i, err; 3747 int i, err;
3736 3748
3737 ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
3738 if (!ssidie)
3739 return -EINVAL;
3740
3741 assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL); 3749 assoc_data = kzalloc(sizeof(*assoc_data) + req->ie_len, GFP_KERNEL);
3742 if (!assoc_data) 3750 if (!assoc_data)
3743 return -ENOMEM; 3751 return -ENOMEM;
3744 3752
3753 rcu_read_lock();
3754 ssidie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID);
3755 if (!ssidie) {
3756 rcu_read_unlock();
3757 kfree(assoc_data);
3758 return -EINVAL;
3759 }
3760 memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]);
3761 assoc_data->ssid_len = ssidie[1];
3762 rcu_read_unlock();
3763
3745 mutex_lock(&ifmgd->mtx); 3764 mutex_lock(&ifmgd->mtx);
3746 3765
3747 if (ifmgd->associated) 3766 if (ifmgd->associated)
@@ -3836,12 +3855,14 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
3836 assoc_data->supp_rates = bss->supp_rates; 3855 assoc_data->supp_rates = bss->supp_rates;
3837 assoc_data->supp_rates_len = bss->supp_rates_len; 3856 assoc_data->supp_rates_len = bss->supp_rates_len;
3838 3857
3858 rcu_read_lock();
3839 ht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION); 3859 ht_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_OPERATION);
3840 if (ht_ie && ht_ie[1] >= sizeof(struct ieee80211_ht_operation)) 3860 if (ht_ie && ht_ie[1] >= sizeof(struct ieee80211_ht_operation))
3841 assoc_data->ap_ht_param = 3861 assoc_data->ap_ht_param =
3842 ((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param; 3862 ((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param;
3843 else 3863 else
3844 ifmgd->flags |= IEEE80211_STA_DISABLE_HT; 3864 ifmgd->flags |= IEEE80211_STA_DISABLE_HT;
3865 rcu_read_unlock();
3845 3866
3846 if (bss->wmm_used && bss->uapsd_supported && 3867 if (bss->wmm_used && bss->uapsd_supported &&
3847 (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) { 3868 (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) {
@@ -3852,9 +3873,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata,
3852 ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED; 3873 ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED;
3853 } 3874 }
3854 3875
3855 memcpy(assoc_data->ssid, ssidie + 2, ssidie[1]);
3856 assoc_data->ssid_len = ssidie[1];
3857
3858 if (req->prev_bssid) 3876 if (req->prev_bssid)
3859 memcpy(assoc_data->prev_bssid, req->prev_bssid, ETH_ALEN); 3877 memcpy(assoc_data->prev_bssid, req->prev_bssid, ETH_ALEN);
3860 3878