diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c index c26221287515..45e7a7ea7690 100644 --- a/net/mac80211/rc80211_minstrel_ht.c +++ b/net/mac80211/rc80211_minstrel_ht.c @@ -1194,6 +1194,54 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) mi->sample_time = jiffies; } +/* + * Check whether an HT/VHT rate from a TX status entry maps to an entry + * in the minstrel_ht rate tables. Values that cannot be represented + * there must not be used for indexing mi->groups[] and the MCS groups. + */ +static bool +minstrel_ht_txstat_rate_valid(struct ieee80211_tx_rate *rate) +{ + unsigned int bw; + + if (!(rate->flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS))) + return true; + + if (rate->flags & IEEE80211_TX_RC_MCS) { + /* minstrel_ht supports up to MINSTREL_MAX_STREAMS streams */ + return rate->idx < MINSTREL_MAX_STREAMS * 8; + } + + /* minstrel_ht has no VHT groups for 160 MHz and wider */ + bw = !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + + 2 * !!(rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH); + if ((rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH) || bw > BW_80) + return false; + + return ieee80211_rate_get_vht_nss(rate) <= MINSTREL_MAX_STREAMS && + ieee80211_rate_get_vht_mcs(rate) < MCS_GROUP_RATES; +} + +static bool +minstrel_ht_ri_txstat_rate_valid(struct rate_info *rate) +{ + if (!(rate->flags & (RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_VHT_MCS))) + return true; + + if (rate->flags & RATE_INFO_FLAGS_MCS) { + /* minstrel_ht supports up to MINSTREL_MAX_STREAMS streams */ + return rate->mcs < MINSTREL_MAX_STREAMS * 8; + } + + /* minstrel_ht has VHT groups only for 20/40/80 MHz */ + if (rate->bw != RATE_INFO_BW_20 && rate->bw != RATE_INFO_BW_40 && + rate->bw != RATE_INFO_BW_80) + return false; + + return rate->nss <= MINSTREL_MAX_STREAMS && + rate->mcs < MCS_GROUP_RATES; +} + static bool minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, struct ieee80211_tx_rate *rate) @@ -1206,9 +1254,8 @@ minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, if (!rate->count) return false; - if (rate->flags & IEEE80211_TX_RC_MCS || - rate->flags & IEEE80211_TX_RC_VHT_MCS) - return true; + if (rate->flags & (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS)) + return minstrel_ht_txstat_rate_valid(rate); for (i = 0; i < ARRAY_SIZE(mp->cck_rates); i++) if (rate->idx == mp->cck_rates[i]) @@ -1236,9 +1283,9 @@ minstrel_ht_ri_txstat_valid(struct minstrel_priv *mp, if (!rate_status->try_count) return false; - if (rate_status->rate_idx.flags & RATE_INFO_FLAGS_MCS || - rate_status->rate_idx.flags & RATE_INFO_FLAGS_VHT_MCS) - return true; + if (rate_status->rate_idx.flags & + (RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_VHT_MCS)) + return minstrel_ht_ri_txstat_rate_valid(&rate_status->rate_idx); for (i = 0; i < ARRAY_SIZE(mp->cck_rates); i++) { if (rate_status->rate_idx.legacy == diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 3d811f652603..414979e2a1d9 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -1164,6 +1164,73 @@ void ieee80211_tx_status_skb(struct ieee80211_hw *hw, struct sk_buff *skb) } EXPORT_SYMBOL(ieee80211_tx_status_skb); +/* + * Check whether the HT/VHT rate information in a TX status entry is + * valid for its encoding. This is not specific to any rate control + * algorithm; all status consumers rely on the values being sane. + */ +static bool ieee80211_tx_status_rate_info_valid(const struct rate_info *rate) +{ + if (rate->flags & RATE_INFO_FLAGS_MCS) + return rate->mcs < 32; + + if (rate->flags & RATE_INFO_FLAGS_VHT_MCS) + return rate->nss >= 1 && rate->nss <= 8 && rate->mcs <= 11; + + return true; +} + +static bool +ieee80211_tx_status_tx_rate_valid(const struct ieee80211_tx_rate *rate) +{ + if (rate->flags & IEEE80211_TX_RC_MCS) + return rate->idx < 32; + + if (rate->flags & IEEE80211_TX_RC_VHT_MCS) + return ieee80211_rate_get_vht_mcs(rate) <= 11; + + return true; +} + +/* + * Drop TX status rate entries that don't describe a valid rate. The + * status information is used by rate control and by other mac80211 + * code, and a malformed entry must not be able to corrupt state beyond + * the driver that reported it. + */ +static void +ieee80211_tx_status_drop_invalid_rates(struct ieee80211_tx_status *status) +{ + int i; + + for (i = 0; i < status->n_rates; i++) { + struct ieee80211_rate_status *rs = &status->rates[i]; + + if (ieee80211_tx_status_rate_info_valid(&rs->rate_idx)) + continue; + + rs->try_count = 0; + memset(&rs->rate_idx, 0, sizeof(rs->rate_idx)); + } + + if (!status->info) + return; + + for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { + struct ieee80211_tx_rate *rate; + + rate = &status->info->status.rates[i]; + if (rate->idx < 0) + break; + + if (ieee80211_tx_status_tx_rate_valid(rate)) + continue; + + rate->idx = -1; + rate->count = 0; + } +} + void ieee80211_tx_status_ext(struct ieee80211_hw *hw, struct ieee80211_tx_status *status) { @@ -1176,6 +1243,8 @@ void ieee80211_tx_status_ext(struct ieee80211_hw *hw, bool acked, noack_success, ack_signal_valid; u16 tx_time_est; + ieee80211_tx_status_drop_invalid_rates(status); + if (pubsta) { sta = container_of(pubsta, struct sta_info, sta); @@ -1300,6 +1369,7 @@ void ieee80211_tx_rate_update(struct ieee80211_hw *hw, .sta = pubsta, }; + ieee80211_tx_status_drop_invalid_rates(&status); rate_control_tx_status(local, &status); if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL))