Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mac80211: fix handling TIM IE when stations disconnect

When a station disconnects with frames still pending, we clear
the TIM bit, but too late - it's only cleared when the station
is already removed from the driver, and thus the driver can get
confused (and hwsim will loudly complain.)

Fix this by clearing the TIM bit earlier, when the station has
been unlinked but not removed from the driver yet. To do this,
refactor the TIM recalculation to in that case ignore traffic
and simply assume no pending traffic - this is correct for the
disconnected station even though the frames haven't been freed
yet at that point.

This patch isn't needed for current drivers though as they don't
check the station argument to the set_tim() operation and thus
don't really run into the possible confusion.

Reported-by: Jouni Malinen <j@w1.fi>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

+13 -3
+13 -3
net/mac80211/sta_info.c
··· 116 116 clear_sta_flag(sta, WLAN_STA_PS_DELIVER); 117 117 118 118 atomic_dec(&ps->num_sta_ps); 119 - sta_info_recalc_tim(sta); 120 119 } 121 120 122 121 for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { ··· 624 625 } 625 626 } 626 627 627 - void sta_info_recalc_tim(struct sta_info *sta) 628 + static void __sta_info_recalc_tim(struct sta_info *sta, bool ignore_pending) 628 629 { 629 630 struct ieee80211_local *local = sta->local; 630 631 struct ps_data *ps; ··· 666 667 if (ignore_for_tim == BIT(IEEE80211_NUM_ACS) - 1) 667 668 ignore_for_tim = 0; 668 669 670 + if (ignore_pending) 671 + ignore_for_tim = BIT(IEEE80211_NUM_ACS) - 1; 672 + 669 673 for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { 670 674 unsigned long tids; 671 675 ··· 697 695 else 698 696 __bss_tim_clear(ps->tim, id); 699 697 700 - if (local->ops->set_tim) { 698 + if (local->ops->set_tim && !WARN_ON(sta->dead)) { 701 699 local->tim_in_locked_section = true; 702 700 drv_set_tim(local, &sta->sta, indicate_tim); 703 701 local->tim_in_locked_section = false; ··· 705 703 706 704 out_unlock: 707 705 spin_unlock_bh(&local->tim_lock); 706 + } 707 + 708 + void sta_info_recalc_tim(struct sta_info *sta) 709 + { 710 + __sta_info_recalc_tim(sta, false); 708 711 } 709 712 710 713 static bool sta_info_buffer_expired(struct sta_info *sta, struct sk_buff *skb) ··· 894 887 895 888 /* now keys can no longer be reached */ 896 889 ieee80211_free_sta_keys(local, sta); 890 + 891 + /* disable TIM bit - last chance to tell driver */ 892 + __sta_info_recalc_tim(sta, true); 897 893 898 894 sta->dead = true; 899 895