diff options
author | Johannes Berg <johannes.berg@intel.com> | 2012-11-29 12:45:18 +0100 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2012-11-30 13:41:26 +0100 |
commit | c604b9f219422e969fe371cc7259de34c3c5601d (patch) | |
tree | 01f8773dbdc25cc7d044b0e371acf3a763af1604 /net/mac80211/util.c | |
parent | f94f8b168cf2e46da180bbba2afd626d7af0579d (diff) |
mac80211: make ieee80211_build_preq_ies safer
Instead of assuming 200 bytes are always enough for
all the IEs we add, give the length of the buffer
to the function and warn instead of overrunning.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/util.c')
-rw-r--r-- | net/mac80211/util.c | 34 |
1 files changed, 27 insertions, 7 deletions
diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 6e4c8bd5bfee..f119b1b6c1a7 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1107,12 +1107,12 @@ void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, } int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, - const u8 *ie, size_t ie_len, + size_t buffer_len, const u8 *ie, size_t ie_len, enum ieee80211_band band, u32 rate_mask, u8 channel) { struct ieee80211_supported_band *sband; - u8 *pos; + u8 *pos = buffer, *end = buffer + buffer_len; size_t offset = 0, noffset; int supp_rates_len, i; u8 rates[32]; @@ -1123,8 +1123,6 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, if (WARN_ON_ONCE(!sband)) return 0; - pos = buffer; - num_rates = 0; for (i = 0; i < sband->n_bitrates; i++) { if ((BIT(i) & rate_mask) == 0) @@ -1134,6 +1132,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, supp_rates_len = min_t(int, num_rates, 8); + if (end - pos < 2 + supp_rates_len) + goto out_err; *pos++ = WLAN_EID_SUPP_RATES; *pos++ = supp_rates_len; memcpy(pos, rates, supp_rates_len); @@ -1150,6 +1150,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, before_extrates, ARRAY_SIZE(before_extrates), offset); + if (end - pos < noffset - offset) + goto out_err; memcpy(pos, ie + offset, noffset - offset); pos += noffset - offset; offset = noffset; @@ -1157,6 +1159,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, ext_rates_len = num_rates - supp_rates_len; if (ext_rates_len > 0) { + if (end - pos < 2 + ext_rates_len) + goto out_err; *pos++ = WLAN_EID_EXT_SUPP_RATES; *pos++ = ext_rates_len; memcpy(pos, rates + supp_rates_len, ext_rates_len); @@ -1164,6 +1168,8 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, } if (channel && sband->band == IEEE80211_BAND_2GHZ) { + if (end - pos < 3) + goto out_err; *pos++ = WLAN_EID_DS_PARAMS; *pos++ = 1; *pos++ = channel; @@ -1182,14 +1188,19 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, noffset = ieee80211_ie_split(ie, ie_len, before_ht, ARRAY_SIZE(before_ht), offset); + if (end - pos < noffset - offset) + goto out_err; memcpy(pos, ie + offset, noffset - offset); pos += noffset - offset; offset = noffset; } - if (sband->ht_cap.ht_supported) + if (sband->ht_cap.ht_supported) { + if (end - pos < 2 + sizeof(struct ieee80211_ht_cap)) + goto out_err; pos = ieee80211_ie_build_ht_cap(pos, &sband->ht_cap, sband->ht_cap.cap); + } /* * If adding more here, adjust code in main.c @@ -1199,15 +1210,23 @@ int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, /* add any remaining custom IEs */ if (ie && ie_len) { noffset = ie_len; + if (end - pos < noffset - offset) + goto out_err; memcpy(pos, ie + offset, noffset - offset); pos += noffset - offset; } - if (sband->vht_cap.vht_supported) + if (sband->vht_cap.vht_supported) { + if (end - pos < 2 + sizeof(struct ieee80211_vht_cap)) + goto out_err; pos = ieee80211_ie_build_vht_cap(pos, &sband->vht_cap, sband->vht_cap.cap); + } return pos - buffer; + out_err: + WARN_ONCE(1, "not enough space for preq IEs\n"); + return pos - buffer; } struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, @@ -1239,7 +1258,8 @@ struct sk_buff *ieee80211_build_probe_req(struct ieee80211_sub_if_data *sdata, else chan_no = ieee80211_frequency_to_channel(chan->center_freq); - buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len, chan->band, + buf_len = ieee80211_build_preq_ies(local, buf, 200 + ie_len, + ie, ie_len, chan->band, ratemask, chan_no); skb = ieee80211_probereq_get(&local->hw, &sdata->vif, |