[PATCH 2/4] wifi: iwlwifi: mvm: clamp set_freqs iteration to n_nd_channels

From: Michael Bommarito

Date: Fri May 15 2026 - 08:46:57 EST


iwl_mvm_query_set_freqs() iterates over bit positions
0 .. SCAN_OFFLOAD_MATCHING_CHANNELS_LEN * 8 - 1 (= 0..55 on the v2
path, 0..39 on the v1 path) and, for each set bit, performs:

match->channels[n_channels++] =
mvm->nd_channels[i]->center_freq;

without constraining i against mvm->n_nd_channels. The pointer
table mvm->nd_channels is kmemdup()ed at suspend time with
exactly mvm->n_nd_channels entries (whatever the userspace
NL80211_CMD_SET_WOWLAN request supplied as
nd_config->n_channels; typical real-world values are 5..50).
If the firmware response contains any matching_channels[] bit
set at a position >= mvm->n_nd_channels, the indexed load reads
a u8* slot past the end of the pointer-table allocation, then
the immediate ->center_freq dereferences that wild pointer.

The pre-existing caller guard

if (mvm->n_nd_channels < n_channels)
continue;

compares the bitmap's popcount to the table length, not the bit
positions to the table length. A bitmap with three set bits at
positions {50, 51, 52} has popcount 3 and passes the guard
unconditionally, then walks 50+ entries off the end of
mvm->nd_channels.

Reproduced under UML+KASAN via a KUnit harness that lifts the
iteration logic. With nd_channels allocated as 5 entries and
matching_channels bits set at positions 7 (immediate redzone)
and 50 (far OOB), the kernel panics on the wild deref:

Kernel panic - not syncing: Segfault with no mm
RIP: 0033:set_freqs_buggy.constprop.0+0xc1/0x15e

(The selector 0x0033 in the RIP line is UML's user-mode segment;
under UML, in-kernel code runs in ring 3 on the host. The trap
is a kernel-context page fault on the wild-pointer deref.)

Building drivers/net/wireless/intel/iwlwifi/mvm/d3.o under
x86_64 allmodconfig with the fix applied yields no new warnings.

Clamp the iteration upper bound to min(matching-bits-width,
mvm->n_nd_channels) so high-position bits, however the firmware
emitted them, cannot index past the pointer table. Mirror the
fix for the v1 fallback arm.

Cc: stable@xxxxxxxxxxxxxxx
Fixes: 8ed4e659f34c ("iwlwifi: mvm: add channel information to the netdetect notifications")
Signed-off-by: Michael Bommarito <michael.bommarito@xxxxxxxxx>
Assisted-by: Claude:claude-opus-4-7
---
drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
index c17ac62feec3..b04d8dd26cd0 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
@@ -2514,16 +2514,20 @@ static void iwl_mvm_query_set_freqs(struct iwl_mvm *mvm,
IWL_UCODE_TLV_API_SCAN_OFFLOAD_CHANS)) {
struct iwl_scan_offload_profile_match *matches =
(void *)results->matches;
+ int max = min_t(int, SCAN_OFFLOAD_MATCHING_CHANNELS_LEN * 8,
+ mvm->n_nd_channels);

- for (i = 0; i < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN * 8; i++)
+ for (i = 0; i < max; i++)
if (matches[idx].matching_channels[i / 8] & (BIT(i % 8)))
match->channels[n_channels++] =
mvm->nd_channels[i]->center_freq;
} else {
struct iwl_scan_offload_profile_match_v1 *matches =
(void *)results->matches;
+ int max = min_t(int, SCAN_OFFLOAD_MATCHING_CHANNELS_LEN_V1 * 8,
+ mvm->n_nd_channels);

- for (i = 0; i < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN_V1 * 8; i++)
+ for (i = 0; i < max; i++)
if (matches[idx].matching_channels[i / 8] & (BIT(i % 8)))
match->channels[n_channels++] =
mvm->nd_channels[i]->center_freq;
--
2.53.0