mac80211 frees a key after set_key() with DISABLE_KEY whatever the driver returns. When the WMI command to remove the key fails, ath11k returns before it clears peer->keys[], so the peer keeps a pointer to the freed key. While the firmware is down, removing a station frees its keys this way, and the following ASSOC -> AUTH transition passes the freed keys to ath11k_install_key() from ath11k_clear_peer_keys(). Clear the host reference to a disabled key also when the firmware command fails. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.12-01460 Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Signed-off-by: Julius Bairaktaris --- Changes in v2: new patch. drivers/net/wireless/ath/ath11k/mac.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 1a237adaa37d..9c3f577c9562 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -4615,17 +4615,22 @@ static int ath11k_mac_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, is_ap_with_no_sta = (vif->type == NL80211_IFTYPE_AP && !arvif->num_stations); if (flags == WMI_KEY_PAIRWISE || cmd == SET_KEY || is_ap_with_no_sta) { + /* mac80211 frees a disabled key whatever this returns, so + * forget it below even if the firmware did not remove it. + */ ret = ath11k_install_key(arvif, key, cmd, peer_addr, flags); if (ret) { ath11k_warn(ab, "ath11k_install_key failed (%d)\n", ret); - goto exit; + if (cmd == SET_KEY) + goto exit; } ret = ath11k_dp_peer_rx_pn_replay_config(arvif, peer_addr, cmd, key); if (ret) { ath11k_warn(ab, "failed to offload PN replay detection %d\n", ret); - goto exit; + if (cmd == SET_KEY) + goto exit; } if (flags == WMI_KEY_GROUP && cmd == SET_KEY && is_ap_with_no_sta) -- 2.53.0 mac80211 requires sta_state to succeed for down transitions and warns when the driver returns an error for one. ath11k returns the result of WMI commands for ASSOC -> AUTH and NONE -> NOTEXIST, and these fail with -ESHUTDOWN while the firmware is down. A station removed during a firmware crash then triggers, for the disassociation: Driver is not allowed to fail if the sta_state is transitioning down the list: -108 WARNING: CPU: 1 PID: 5952 at net/mac80211/sta_info.c:1561 ieee80211_sta_eosp+0x119c/0x1668 [mac80211] and for the removal: WARNING: CPU: 2 PID: 2080 at net/mac80211/sta_info.c:1652 sta_set_sinfo+0x1118/0x1144 [mac80211] Call trace: sta_set_sinfo+0x1118/0x1144 [mac80211] (P) sta_info_destroy_addr_bss+0x34/0x48 [mac80211] ieee80211_del_station+0x1c/0x54 [mac80211] nl80211_del_station+0xf4/0x174 [cfg80211] Two of these failures also skip host cleanup. On a non-AP vdev of a device with vdev_start_delay, a failed early vdev stop returns from ath11k_mac_station_remove() before the DP peer cleanup and the station accounting, and ath11k_mac_op_sta_state() then frees the peer with its rx_tid state still in place. For a non-WME station, a failed RTS/CTS update returns from ath11k_station_disassoc() before the peer keys are cleared. Warn and carry on with the host cleanup in both, and return success for every down transition. Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.12-01460 Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices") Signed-off-by: Julius Bairaktaris --- Changes in v2: continue the host cleanup after a failed early vdev stop and a failed RTS/CTS update instead of returning from them. drivers/net/wireless/ath/ath11k/mac.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 9c3f577c9562..a86d1b6c0d73 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -4984,9 +4984,7 @@ static int ath11k_station_disassoc(struct ath11k *ar, if (!sta->wme) { arvif->num_legacy_stations--; - ret = ath11k_recalc_rtscts_prot(arvif); - if (ret) - return ret; + ath11k_recalc_rtscts_prot(arvif); } ret = ath11k_clear_peer_keys(arvif, sta->addr); @@ -9922,10 +9920,8 @@ static int ath11k_mac_station_remove(struct ath11k *ar, arvif->is_started && arvif->vdev_type != WMI_VDEV_TYPE_AP) { ret = ath11k_mac_stop_vdev_early(ar->hw, vif); - if (ret) { + if (ret) ath11k_warn(ab, "failed to do early vdev stop: %d\n", ret); - return ret; - } } ath11k_dp_peer_cleanup(ar, arvif->vdev_id, sta->addr); @@ -10079,6 +10075,10 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, sta->addr); } + /* mac80211 does not let a down transition fail */ + if (new_state < old_state) + ret = 0; + mutex_unlock(&ar->conf_mutex); return ret; } -- 2.53.0