summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/intel
diff options
context:
space:
mode:
authorMiri Korenblit <miriam.rachel.korenblit@intel.com>2026-09-23 16:04:50 +0300
committerMiri Korenblit <miriam.rachel.korenblit@intel.com>2026-09-30 11:18:31 +0300
commit87ba1ab0bf4aa7bc6cc30b0c77960cb8032645fc (patch)
treef6f9a423ca55b9971bd942b74baf07459f903f76 /drivers/net/wireless/intel
parenta97c65a64fe7b8979754ddc415566172f298b8e0 (diff)
downloadlinux-next-87ba1ab0bf4aa7bc6cc30b0c77960cb8032645fc.tar.gz
linux-next-87ba1ab0bf4aa7bc6cc30b0c77960cb8032645fc.zip
wifi: iwlwifi: support net detect match info version 3
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 <ilan.peer@intel.com> Link: https://patch.msgid.link/20260923160318.07738ca44b54.I58162891250af9f46220fd01db667460e6dd621b@changeid Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Diffstat (limited to 'drivers/net/wireless/intel')
-rw-r--r--drivers/net/wireless/intel/iwlwifi/fw/api/scan.h28
-rw-r--r--drivers/net/wireless/intel/iwlwifi/mvm/d3.c89
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
@@ -1281,10 +1282,30 @@ struct iwl_scan_offload_profiles_query_v1 {
} __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 bd52f2ebd3ba..2abc4bc3eb34 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
@@ -2440,6 +2440,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)
@@ -2459,12 +2465,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;
@@ -2499,13 +2510,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;
@@ -2517,34 +2534,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;
}
/**
@@ -2817,8 +2858,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;