Thread (16 messages) 16 messages, 1 author, 17d ago

[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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help