Protect vif->scan_req with a spinlock to serialize scan submission, completion, disconnect, and interface-stop paths. This prevents scan completion and abort handling from notifying cfg80211 more than once or accessing a cleared scan request. Without synchronization, one thread can set scan_req to NULL while another dereferences it, causing a kernel panic. Co-developed-by: Uwe Schnick Signed-off-by: Uwe Schnick Signed-off-by: Vishal Gupta --- v1: https://lore.kernel.org/linux-wireless/20261026131631.1229744-1-vishal.gupta_1@philips.com/ Changes in v2: - Fix subject prefix ("wifi: ") - Add Co-developed-by for Uwe Schnick and fix Signed-off-by order - Keep local variable declarations in reverse xmas tree order - Drop redundant "req = NULL" stores after cfg80211_scan_done() - Move scan_req_lock next to scan_req in struct ath6kl_vif drivers/net/wireless/ath/ath6kl/cfg80211.c | 44 ++++++++++++++++------ drivers/net/wireless/ath/ath6kl/core.h | 2 + 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index c91560d559c51..4e620f8b6d69a 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -865,15 +865,20 @@ void ath6kl_cfg80211_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, u8 assoc_resp_len, u8 *assoc_info, u16 proto_reason) { + struct cfg80211_scan_request *req; struct ath6kl *ar = vif->ar; - if (vif->scan_req) { + spin_lock_bh(&vif->scan_req_lock); + req = vif->scan_req; + vif->scan_req = NULL; + spin_unlock_bh(&vif->scan_req_lock); + + if (req) { struct cfg80211_scan_info info = { .aborted = true, }; - cfg80211_scan_done(vif->scan_req, &info); - vif->scan_req = NULL; + cfg80211_scan_done(req, &info); } if (vif->nw_type & ADHOC_NETWORK) { @@ -1060,7 +1065,9 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, if (test_bit(CONNECTED, &vif->flags)) force_fg_scan = 1; + spin_lock_bh(&vif->scan_req_lock); vif->scan_req = request; + spin_unlock_bh(&vif->scan_req_lock); ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx, WMI_LONG_SCAN, force_fg_scan, @@ -1071,7 +1078,9 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, request->rates); if (ret) { ath6kl_err("failed to start scan: %d\n", ret); + spin_lock_bh(&vif->scan_req_lock); vif->scan_req = NULL; + spin_unlock_bh(&vif->scan_req_lock); } kfree(channels); @@ -1081,23 +1090,29 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted) { - struct ath6kl *ar = vif->ar; + struct cfg80211_scan_request *req; struct cfg80211_scan_info info = { .aborted = aborted, }; + struct ath6kl *ar = vif->ar; int i; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: status%s\n", __func__, aborted ? " aborted" : ""); - if (!vif->scan_req) + spin_lock_bh(&vif->scan_req_lock); + req = vif->scan_req; + vif->scan_req = NULL; + spin_unlock_bh(&vif->scan_req_lock); + + if (!req) return; if (aborted) goto out; - if (vif->scan_req->n_ssids && vif->scan_req->ssids[0].ssid_len) { - for (i = 0; i < vif->scan_req->n_ssids; i++) { + if (req->n_ssids && req->ssids[0].ssid_len) { + for (i = 0; i < req->n_ssids; i++) { ath6kl_wmi_probedssid_cmd(ar->wmi, vif->fw_vif_idx, i, DISABLE_SSID_FLAG, 0, NULL); @@ -1105,8 +1120,7 @@ void ath6kl_cfg80211_scan_complete_event(struct ath6kl_vif *vif, bool aborted) } out: - cfg80211_scan_done(vif->scan_req, &info); - vif->scan_req = NULL; + cfg80211_scan_done(req, &info); } void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, @@ -3601,6 +3615,7 @@ static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif) set_bit(WMM_ENABLED, &vif->flags); spin_lock_init(&vif->if_lock); + spin_lock_init(&vif->scan_req_lock); INIT_LIST_HEAD(&vif->mc_filter); @@ -3609,6 +3624,7 @@ static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif) void ath6kl_cfg80211_vif_stop(struct ath6kl_vif *vif, bool wmi_ready) { + struct cfg80211_scan_request *req; static u8 bcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; bool discon_issued; @@ -3629,13 +3645,17 @@ void ath6kl_cfg80211_vif_stop(struct ath6kl_vif *vif, bool wmi_ready) 0, NULL, 0); } - if (vif->scan_req) { + spin_lock_bh(&vif->scan_req_lock); + req = vif->scan_req; + vif->scan_req = NULL; + spin_unlock_bh(&vif->scan_req_lock); + + if (req) { struct cfg80211_scan_info info = { .aborted = true, }; - cfg80211_scan_done(vif->scan_req, &info); - vif->scan_req = NULL; + cfg80211_scan_done(req, &info); } /* need to clean up enhanced bmiss detection fw state */ diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index a0b236eeeab54..b8149abfe7893 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -629,6 +629,8 @@ struct ath6kl_vif { struct timer_list disconnect_timer; struct timer_list sched_scan_timer; + /* Protects scan_req */ + spinlock_t scan_req_lock; struct cfg80211_scan_request *scan_req; enum sme_state sme_state; int reconnect_flag; -- 2.43.0