wifi: mac80211: limit reorder_buf_filtered to avoid UBSAN warning
authorPing-Ke Shih <pkshih@realtek.com>
Fri, 18 Aug 2023 01:40:04 +0000 (09:40 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 30 Aug 2023 14:11:05 +0000 (16:11 +0200)
commit b98c16107cc1647242abbd11f234c05a3a5864f6 upstream.

The commit 06470f7468c8 ("mac80211: add API to allow filtering frames in BA sessions")
added reorder_buf_filtered to mark frames filtered by firmware, and it
can only work correctly if hw.max_rx_aggregation_subframes <= 64 since
it stores the bitmap in a u64 variable.

However, new HE or EHT devices can support BlockAck number up to 256 or
1024, and then using a higher subframe index leads UBSAN warning:

 UBSAN: shift-out-of-bounds in net/mac80211/rx.c:1129:39
 shift exponent 215 is too large for 64-bit type 'long long unsigned int'
 Call Trace:
  <IRQ>
  dump_stack_lvl+0x48/0x70
  dump_stack+0x10/0x20
  __ubsan_handle_shift_out_of_bounds+0x1ac/0x360
  ieee80211_release_reorder_frame.constprop.0.cold+0x64/0x69 [mac80211]
  ieee80211_sta_reorder_release+0x9c/0x400 [mac80211]
  ieee80211_prepare_and_rx_handle+0x1234/0x1420 [mac80211]
  ieee80211_rx_list+0xaef/0xf60 [mac80211]
  ieee80211_rx_napi+0x53/0xd0 [mac80211]

Since only old hardware that supports <=64 BlockAck uses
ieee80211_mark_rx_ba_filtered_frames(), limit the use as it is, so add a
WARN_ONCE() and comment to note to avoid using this function if hardware
capability is not suitable.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://lore.kernel.org/r/20230818014004.16177-1-pkshih@realtek.com
[edit commit message]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/net/mac80211.h
net/mac80211/rx.c

index 72b739d..8a338c3 100644 (file)
@@ -6444,6 +6444,7 @@ void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap,
  * marks frames marked in the bitmap as having been filtered. Afterwards, it
  * checks if any frames in the window starting from @ssn can now be released
  * (in case they were only waiting for frames that were filtered.)
+ * (Only work correctly if @max_rx_aggregation_subframes <= 64 frames)
  */
 void ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *pubsta, u8 tid,
                                          u16 ssn, u64 filtered,
index 0f81492..55dc061 100644 (file)
@@ -1102,7 +1102,8 @@ static inline bool ieee80211_rx_reorder_ready(struct tid_ampdu_rx *tid_agg_rx,
        struct sk_buff *tail = skb_peek_tail(frames);
        struct ieee80211_rx_status *status;
 
-       if (tid_agg_rx->reorder_buf_filtered & BIT_ULL(index))
+       if (tid_agg_rx->reorder_buf_filtered &&
+           tid_agg_rx->reorder_buf_filtered & BIT_ULL(index))
                return true;
 
        if (!tail)
@@ -1143,7 +1144,8 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata,
        }
 
 no_frame:
-       tid_agg_rx->reorder_buf_filtered &= ~BIT_ULL(index);
+       if (tid_agg_rx->reorder_buf_filtered)
+               tid_agg_rx->reorder_buf_filtered &= ~BIT_ULL(index);
        tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num);
 }
 
@@ -4162,6 +4164,7 @@ void ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *pubsta, u8 tid,
                                          u16 ssn, u64 filtered,
                                          u16 received_mpdus)
 {
+       struct ieee80211_local *local;
        struct sta_info *sta;
        struct tid_ampdu_rx *tid_agg_rx;
        struct sk_buff_head frames;
@@ -4179,6 +4182,11 @@ void ieee80211_mark_rx_ba_filtered_frames(struct ieee80211_sta *pubsta, u8 tid,
 
        sta = container_of(pubsta, struct sta_info, sta);
 
+       local = sta->sdata->local;
+       WARN_ONCE(local->hw.max_rx_aggregation_subframes > 64,
+                 "RX BA marker can't support max_rx_aggregation_subframes %u > 64\n",
+                 local->hw.max_rx_aggregation_subframes);
+
        if (!ieee80211_rx_data_set_sta(&rx, sta, -1))
                return;