| From dcd479e10a0510522a5d88b29b8f79ea3467d501 Mon Sep 17 00:00:00 2001 |
| From: Johannes Berg <johannes.berg@intel.com> |
| Date: Fri, 9 Oct 2020 14:17:11 +0200 |
| Subject: mac80211: always wind down STA state |
| |
| From: Johannes Berg <johannes.berg@intel.com> |
| |
| commit dcd479e10a0510522a5d88b29b8f79ea3467d501 upstream. |
| |
| When (for example) an IBSS station is pre-moved to AUTHORIZED |
| before it's inserted, and then the insertion fails, we don't |
| clean up the fast RX/TX states that might already have been |
| created, since we don't go through all the state transitions |
| again on the way down. |
| |
| Do that, if it hasn't been done already, when the station is |
| freed. I considered only freeing the fast TX/RX state there, |
| but we might add more state so it's more robust to wind down |
| the state properly. |
| |
| Note that we warn if the station was ever inserted, it should |
| have been properly cleaned up in that case, and the driver |
| will probably not like things happening out of order. |
| |
| Reported-by: syzbot+2e293dbd67de2836ba42@syzkaller.appspotmail.com |
| Link: https://lore.kernel.org/r/20201009141710.7223b322a955.I95bd08b9ad0e039c034927cce0b75beea38e059b@changeid |
| Signed-off-by: Johannes Berg <johannes.berg@intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| net/mac80211/sta_info.c | 18 ++++++++++++++++++ |
| 1 file changed, 18 insertions(+) |
| |
| --- a/net/mac80211/sta_info.c |
| +++ b/net/mac80211/sta_info.c |
| @@ -258,6 +258,24 @@ struct sta_info *sta_info_get_by_idx(str |
| */ |
| void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) |
| { |
| + /* |
| + * If we had used sta_info_pre_move_state() then we might not |
| + * have gone through the state transitions down again, so do |
| + * it here now (and warn if it's inserted). |
| + * |
| + * This will clear state such as fast TX/RX that may have been |
| + * allocated during state transitions. |
| + */ |
| + while (sta->sta_state > IEEE80211_STA_NONE) { |
| + int ret; |
| + |
| + WARN_ON_ONCE(test_sta_flag(sta, WLAN_STA_INSERTED)); |
| + |
| + ret = sta_info_move_state(sta, sta->sta_state - 1); |
| + if (WARN_ONCE(ret, "sta_info_move_state() returned %d\n", ret)) |
| + break; |
| + } |
| + |
| if (sta->rate_ctrl) |
| rate_control_free_sta(sta); |
| |