[PATCH iwlwifi-next 07/15] wifi: iwlwifi: support net detect match info version 3
flat view
COLD17d
IN LINUX-NEXT: 1 (0M)
From: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Date: 2026-09-23 13:03:14
Subsystem:
intel wireless wifi link (iwlwifi), the rest · Maintainers:
Miri Korenblit, Linus Torvalds
1 review trailer; queued in linux-next as 87ba1ab0bf4a on 2026-09-30.
Since API 77, firmware reports scan offload profile matches using SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_3, which has more channels (16 byte instead of 7). Somehow we missed this change, and the driver only handles the 5- and 7-byte layouts, so on such firmware it misparsed the match (reading band as energy) and could not recover matches beyond the first 56 channels. Add the version 3 match structure and select it based on the SCAN_OFFLOAD_PROFILES_QUERY_CMD notification version (in MVM, in MLD, this is the only version supported). In MVM, while walking the matched-channels bitmap, bound the channel index with IWL_FW_CHECK so firmware that sets a bit beyond n_nd_channels can no longer trigger an out-of-bounds read of nd_channels[]. Reviewed-by: Ilan Peer <redacted> Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com> --- .../net/wireless/intel/iwlwifi/fw/api/scan.h | 28 +++++- drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 89 ++++++++++++++----- 2 files changed, 93 insertions(+), 24 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
index cb6217763bd1..8994a7b840ef 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h
+++ b/drivers/net/wireless/intel/iwlwifi/fw/api/scan.h@@ -1234,7 +1234,8 @@ struct iwl_umac_scan_complete { } __packed; /* SCAN_COMPLETE_NTF_UMAC_API_S_VER_1 */ #define SCAN_OFFLOAD_MATCHING_CHANNELS_LEN_V1 5 -#define SCAN_OFFLOAD_MATCHING_CHANNELS_LEN 7 +#define SCAN_OFFLOAD_MATCHING_CHANNELS_LEN_V2 7 +#define SCAN_OFFLOAD_MATCHING_CHANNELS_LEN 16 /** * struct iwl_scan_offload_profile_match_v1 - match information
@@ -1280,11 +1281,31 @@ struct iwl_scan_offload_profiles_query_v1 { struct iwl_scan_offload_profile_match_v1 matches[]; } __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_2 */ +/** + * struct iwl_scan_offload_profile_match_v2 - match information + * @bssid: matched bssid + * @reserved: reserved + * @channel: channel where the match occurred + * @energy: energy + * @matching_feature: feature matches + * @matching_channels: bitmap of channels that matched, referencing + * the channels passed in the scan offload request. + */ +struct iwl_scan_offload_profile_match_v2 { + u8 bssid[ETH_ALEN]; + __le16 reserved; + u8 channel; + u8 energy; + u8 matching_feature; + u8 matching_channels[SCAN_OFFLOAD_MATCHING_CHANNELS_LEN_V2]; +} __packed; /* SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_2 */ + /** * struct iwl_scan_offload_profile_match - match information * @bssid: matched bssid * @reserved: reserved * @channel: channel where the match occurred + * @band: band where the match occurred * @energy: energy * @matching_feature: feature matches * @matching_channels: bitmap of channels that matched, referencing
@@ -1294,10 +1315,11 @@ struct iwl_scan_offload_profile_match { u8 bssid[ETH_ALEN]; __le16 reserved; u8 channel; + u8 band; u8 energy; u8 matching_feature; u8 matching_channels[SCAN_OFFLOAD_MATCHING_CHANNELS_LEN]; -} __packed; /* SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_2 */ +} __packed; /* SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_3 */ /** * struct iwl_scan_offload_match_info - match results information
@@ -1322,7 +1344,7 @@ struct iwl_scan_offload_match_info { u8 self_recovery; __le16 reserved; struct iwl_scan_offload_profile_match matches[IWL_SCAN_MAX_PROFILES_V2]; -} __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_3 and +} __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_5 and * SCAN_OFFLOAD_MATCH_INFO_NOTIFICATION_S_VER_1 */
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
index 6b11fa32ea5c..c27265926ca4 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c@@ -2438,6 +2438,12 @@ struct iwl_mvm_nd_results { u8 matches[ND_QUERY_BUF_LEN]; }; +static bool iwl_mvm_nd_match_info_v3(struct iwl_mvm *mvm) +{ + return iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, + SCAN_OFFLOAD_PROFILES_QUERY_CMD, 0) >= 4; +} + static int iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm, struct iwl_mvm_nd_results *results)
@@ -2457,12 +2463,17 @@ iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm, return ret; } - if (fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_SCAN_OFFLOAD_CHANS)) { + if (iwl_mvm_nd_match_info_v3(mvm)) { matches_len = sizeof(struct iwl_scan_offload_profile_match) * max_profiles; query_len = offsetof(struct iwl_scan_offload_match_info, matches) + matches_len; + } else if (fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_SCAN_OFFLOAD_CHANS)) { + matches_len = sizeof(struct iwl_scan_offload_profile_match_v2) * + max_profiles; + query_len = offsetof(struct iwl_scan_offload_match_info, + matches) + matches_len; } else { matches_len = sizeof(struct iwl_scan_offload_profile_match_v1) * max_profiles;
@@ -2497,13 +2508,19 @@ static int iwl_mvm_query_num_match_chans(struct iwl_mvm *mvm, { int n_chans = 0, i; - if (fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_SCAN_OFFLOAD_CHANS)) { + if (iwl_mvm_nd_match_info_v3(mvm)) { struct iwl_scan_offload_profile_match *matches = (void *)results->matches; for (i = 0; i < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN; i++) n_chans += hweight8(matches[idx].matching_channels[i]); + } else if (fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_SCAN_OFFLOAD_CHANS)) { + struct iwl_scan_offload_profile_match_v2 *matches = + (void *)results->matches; + + for (i = 0; i < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN_V2; i++) + n_chans += hweight8(matches[idx].matching_channels[i]); } else { struct iwl_scan_offload_profile_match_v1 *matches = (void *)results->matches;
@@ -2515,34 +2532,58 @@ static int iwl_mvm_query_num_match_chans(struct iwl_mvm *mvm, return n_chans; } +static void iwl_mvm_set_matching_freqs(struct iwl_mvm *mvm, + const u8 *matching_channels, + size_t num_bytes, + struct cfg80211_wowlan_nd_match *match) +{ + int n_channels = 0; + + for (int i = 0; i < num_bytes * 8; i++) { + if (!(matching_channels[i / 8] & BIT(i % 8))) + continue; + if (IWL_FW_CHECK(mvm, i >= mvm->n_nd_channels, + "FW matched channel bit %d beyond n_nd_channels %d\n", + i, mvm->n_nd_channels)) + break; + match->channels[n_channels++] = + mvm->nd_channels[i]->center_freq; + } + /* We may have ended up with fewer channels than we allocated. */ + match->n_channels = n_channels; +} + static void iwl_mvm_query_set_freqs(struct iwl_mvm *mvm, struct iwl_mvm_nd_results *results, struct cfg80211_wowlan_nd_match *match, int idx) { - int i; - int n_channels = 0; - - if (fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_SCAN_OFFLOAD_CHANS)) { + if (iwl_mvm_nd_match_info_v3(mvm)) { struct iwl_scan_offload_profile_match *matches = (void *)results->matches; - for (i = 0; i < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN * 8; i++) - if (matches[idx].matching_channels[i / 8] & (BIT(i % 8))) - match->channels[n_channels++] = - mvm->nd_channels[i]->center_freq; + iwl_mvm_set_matching_freqs(mvm, + matches[idx].matching_channels, + sizeof(matches[idx].matching_channels), + match); + } else if (fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_SCAN_OFFLOAD_CHANS)) { + struct iwl_scan_offload_profile_match_v2 *matches = + (void *)results->matches; + + iwl_mvm_set_matching_freqs(mvm, + matches[idx].matching_channels, + sizeof(matches[idx].matching_channels), + match); } else { struct iwl_scan_offload_profile_match_v1 *matches = (void *)results->matches; - for (i = 0; i < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN_V1 * 8; i++) - if (matches[idx].matching_channels[i / 8] & (BIT(i % 8))) - match->channels[n_channels++] = - mvm->nd_channels[i]->center_freq; + iwl_mvm_set_matching_freqs(mvm, + matches[idx].matching_channels, + sizeof(matches[idx].matching_channels), + match); } - /* We may have ended up with fewer channels than we allocated. */ - match->n_channels = n_channels; } /**
@@ -2815,8 +2856,14 @@ static void iwl_mvm_nd_match_info_handler(struct iwl_mvm *mvm, struct iwl_wowlan_status_data *status = d3_data->status; struct ieee80211_vif *vif = iwl_mvm_get_bss_vif(mvm); struct iwl_mvm_nd_results *results = d3_data->nd_results; - size_t i, matches_len = sizeof(struct iwl_scan_offload_profile_match) * - iwl_umac_scan_get_max_profiles(mvm->fw); + size_t i, matches_len; + + if (iwl_mvm_nd_match_info_v3(mvm)) + matches_len = sizeof(struct iwl_scan_offload_profile_match) * + iwl_umac_scan_get_max_profiles(mvm->fw); + else + matches_len = sizeof(struct iwl_scan_offload_profile_match_v2) * + iwl_umac_scan_get_max_profiles(mvm->fw); if (IS_ERR_OR_NULL(vif)) return;
--
2.34.1