diff options
author | Johannes Berg <johannes.berg@intel.com> | 2012-11-28 19:25:20 -0500 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2012-11-30 07:42:20 -0500 |
commit | 9caf03640279e64d0ba36539b42daa1b43a49486 (patch) | |
tree | cb094a4a577f61421d1b402e16f0e68f151d5726 /drivers | |
parent | b9a9ada14aab17f08c1d9735601f1097cdcfc6de (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 'drivers')
-rw-r--r-- | drivers/net/wireless/libertas/cfg.c | 9 | ||||
-rw-r--r-- | drivers/net/wireless/mwifiex/sta_ioctl.c | 35 |
2 files changed, 34 insertions, 10 deletions
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index ec36868f6fc5..ec6d5d6b452e 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c | |||
@@ -298,6 +298,7 @@ static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss) | |||
298 | const u8 *rates_eid, *ext_rates_eid; | 298 | const u8 *rates_eid, *ext_rates_eid; |
299 | int n = 0; | 299 | int n = 0; |
300 | 300 | ||
301 | rcu_read_lock(); | ||
301 | rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); | 302 | rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); |
302 | ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); | 303 | ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES); |
303 | 304 | ||
@@ -325,6 +326,7 @@ static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss) | |||
325 | *tlv++ = 0x96; | 326 | *tlv++ = 0x96; |
326 | n = 4; | 327 | n = 4; |
327 | } | 328 | } |
329 | rcu_read_unlock(); | ||
328 | 330 | ||
329 | rate_tlv->header.len = cpu_to_le16(n); | 331 | rate_tlv->header.len = cpu_to_le16(n); |
330 | return sizeof(rate_tlv->header) + n; | 332 | return sizeof(rate_tlv->header) + n; |
@@ -1140,11 +1142,13 @@ static int lbs_associate(struct lbs_private *priv, | |||
1140 | cmd->capability = cpu_to_le16(bss->capability); | 1142 | cmd->capability = cpu_to_le16(bss->capability); |
1141 | 1143 | ||
1142 | /* add SSID TLV */ | 1144 | /* add SSID TLV */ |
1145 | rcu_read_lock(); | ||
1143 | ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); | 1146 | ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); |
1144 | if (ssid_eid) | 1147 | if (ssid_eid) |
1145 | pos += lbs_add_ssid_tlv(pos, ssid_eid + 2, ssid_eid[1]); | 1148 | pos += lbs_add_ssid_tlv(pos, ssid_eid + 2, ssid_eid[1]); |
1146 | else | 1149 | else |
1147 | lbs_deb_assoc("no SSID\n"); | 1150 | lbs_deb_assoc("no SSID\n"); |
1151 | rcu_read_unlock(); | ||
1148 | 1152 | ||
1149 | /* add DS param TLV */ | 1153 | /* add DS param TLV */ |
1150 | if (bss->channel) | 1154 | if (bss->channel) |
@@ -1782,7 +1786,7 @@ static int lbs_ibss_join_existing(struct lbs_private *priv, | |||
1782 | struct cfg80211_ibss_params *params, | 1786 | struct cfg80211_ibss_params *params, |
1783 | struct cfg80211_bss *bss) | 1787 | struct cfg80211_bss *bss) |
1784 | { | 1788 | { |
1785 | const u8 *rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); | 1789 | const u8 *rates_eid; |
1786 | struct cmd_ds_802_11_ad_hoc_join cmd; | 1790 | struct cmd_ds_802_11_ad_hoc_join cmd; |
1787 | u8 preamble = RADIO_PREAMBLE_SHORT; | 1791 | u8 preamble = RADIO_PREAMBLE_SHORT; |
1788 | int ret = 0; | 1792 | int ret = 0; |
@@ -1841,6 +1845,8 @@ static int lbs_ibss_join_existing(struct lbs_private *priv, | |||
1841 | 1845 | ||
1842 | /* set rates to the intersection of our rates and the rates in the | 1846 | /* set rates to the intersection of our rates and the rates in the |
1843 | bss */ | 1847 | bss */ |
1848 | rcu_read_lock(); | ||
1849 | rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES); | ||
1844 | if (!rates_eid) { | 1850 | if (!rates_eid) { |
1845 | lbs_add_rates(cmd.bss.rates); | 1851 | lbs_add_rates(cmd.bss.rates); |
1846 | } else { | 1852 | } else { |
@@ -1860,6 +1866,7 @@ static int lbs_ibss_join_existing(struct lbs_private *priv, | |||
1860 | } | 1866 | } |
1861 | } | 1867 | } |
1862 | } | 1868 | } |
1869 | rcu_read_unlock(); | ||
1863 | 1870 | ||
1864 | /* Only v8 and below support setting this */ | 1871 | /* Only v8 and below support setting this */ |
1865 | if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) { | 1872 | if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) { |
diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 24af6ba7d8a1..5d7b83e2dc4d 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c | |||
@@ -158,12 +158,22 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, | |||
158 | struct cfg80211_bss *bss, | 158 | struct cfg80211_bss *bss, |
159 | struct mwifiex_bssdescriptor *bss_desc) | 159 | struct mwifiex_bssdescriptor *bss_desc) |
160 | { | 160 | { |
161 | int ret; | 161 | int ret, beacon_ie_len; |
162 | u8 *beacon_ie; | 162 | u8 *beacon_ie; |
163 | struct mwifiex_bss_priv *bss_priv = (void *)bss->priv; | 163 | struct mwifiex_bss_priv *bss_priv = (void *)bss->priv; |
164 | const struct cfg80211_bss_ies *ies; | ||
165 | |||
166 | rcu_read_lock(); | ||
167 | ies = rcu_dereference(bss->ies); | ||
168 | if (WARN_ON(!ies)) { | ||
169 | /* should never happen */ | ||
170 | rcu_read_unlock(); | ||
171 | return -EINVAL; | ||
172 | } | ||
173 | beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC); | ||
174 | beacon_ie_len = ies->len; | ||
175 | rcu_read_unlock(); | ||
164 | 176 | ||
165 | beacon_ie = kmemdup(bss->information_elements, bss->len_beacon_ies, | ||
166 | GFP_KERNEL); | ||
167 | if (!beacon_ie) { | 177 | if (!beacon_ie) { |
168 | dev_err(priv->adapter->dev, " failed to alloc beacon_ie\n"); | 178 | dev_err(priv->adapter->dev, " failed to alloc beacon_ie\n"); |
169 | return -ENOMEM; | 179 | return -ENOMEM; |
@@ -172,7 +182,7 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, | |||
172 | memcpy(bss_desc->mac_address, bss->bssid, ETH_ALEN); | 182 | memcpy(bss_desc->mac_address, bss->bssid, ETH_ALEN); |
173 | bss_desc->rssi = bss->signal; | 183 | bss_desc->rssi = bss->signal; |
174 | bss_desc->beacon_buf = beacon_ie; | 184 | bss_desc->beacon_buf = beacon_ie; |
175 | bss_desc->beacon_buf_size = bss->len_beacon_ies; | 185 | bss_desc->beacon_buf_size = beacon_ie_len; |
176 | bss_desc->beacon_period = bss->beacon_interval; | 186 | bss_desc->beacon_period = bss->beacon_interval; |
177 | bss_desc->cap_info_bitmap = bss->capability; | 187 | bss_desc->cap_info_bitmap = bss->capability; |
178 | bss_desc->bss_band = bss_priv->band; | 188 | bss_desc->bss_band = bss_priv->band; |
@@ -198,18 +208,23 @@ int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, | |||
198 | static int mwifiex_process_country_ie(struct mwifiex_private *priv, | 208 | static int mwifiex_process_country_ie(struct mwifiex_private *priv, |
199 | struct cfg80211_bss *bss) | 209 | struct cfg80211_bss *bss) |
200 | { | 210 | { |
201 | u8 *country_ie, country_ie_len; | 211 | const u8 *country_ie; |
212 | u8 country_ie_len; | ||
202 | struct mwifiex_802_11d_domain_reg *domain_info = | 213 | struct mwifiex_802_11d_domain_reg *domain_info = |
203 | &priv->adapter->domain_reg; | 214 | &priv->adapter->domain_reg; |
204 | 215 | ||
205 | country_ie = (u8 *)ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); | 216 | rcu_read_lock(); |
206 | 217 | country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); | |
207 | if (!country_ie) | 218 | if (!country_ie) { |
219 | rcu_read_unlock(); | ||
208 | return 0; | 220 | return 0; |
221 | } | ||
209 | 222 | ||
210 | country_ie_len = country_ie[1]; | 223 | country_ie_len = country_ie[1]; |
211 | if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) | 224 | if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) { |
225 | rcu_read_unlock(); | ||
212 | return 0; | 226 | return 0; |
227 | } | ||
213 | 228 | ||
214 | domain_info->country_code[0] = country_ie[2]; | 229 | domain_info->country_code[0] = country_ie[2]; |
215 | domain_info->country_code[1] = country_ie[3]; | 230 | domain_info->country_code[1] = country_ie[3]; |
@@ -223,6 +238,8 @@ static int mwifiex_process_country_ie(struct mwifiex_private *priv, | |||
223 | memcpy((u8 *)domain_info->triplet, | 238 | memcpy((u8 *)domain_info->triplet, |
224 | &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len); | 239 | &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len); |
225 | 240 | ||
241 | rcu_read_unlock(); | ||
242 | |||
226 | if (mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, | 243 | if (mwifiex_send_cmd_async(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, |
227 | HostCmd_ACT_GEN_SET, 0, NULL)) { | 244 | HostCmd_ACT_GEN_SET, 0, NULL)) { |
228 | wiphy_err(priv->adapter->wiphy, | 245 | wiphy_err(priv->adapter->wiphy, |