* [PATCH 0/4] wifi: iwlwifi: harden netdetect resume-path parsing against firmware-controlled inputs (mvm + mld)
@ 2026-05-15 12:10 Michael Bommarito
2026-05-15 12:10 ` [PATCH 1/4] wifi: iwlwifi: mvm: include matches_len in scan-offload-query length check Michael Bommarito
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Michael Bommarito @ 2026-05-15 12:10 UTC (permalink / raw)
To: Miri Korenblit
Cc: Johannes Berg, Emmanuel Grumbach, linux-wireless, linux-kernel,
stable
Four defensive bound-check additions on the WoWLAN net-detect
resume path in iwlwifi, validating firmware-controlled response
lengths and bitmap-bit positions at the host/firmware trust
boundary. Two patches each in the mvm and mld op-modes; both
op-modes are live in current kernels (mvm drives 7000-series
through pre-BE200 hardware; mld drives Wi-Fi 7 / BE200+ when
CONFIG_IWLMLD is enabled).
The series is in the same shape as the recently fixed sibling
commit 744fabc338e8 ("wifi: iwlwifi: mvm: fix potential
out-of-bounds read in iwl_mvm_nd_match_info_handler()"), which
landed in stable on 2026-04-11. Well-behaved firmware should not
trigger any of these, but the host parser should not depend on
that.
Patches 1 + 3: length-tail guard on the firmware response.
iwl_mvm_netdetect_query_results() and
iwl_mld_netdetect_match_info_handler() validate only the fixed
header size of the response/notification, then memcpy the flex-
array tail unconditionally. A response of exactly query_len /
sizeof(*notif) bytes passes the guard and the memcpy reads
matches_len / NETDETECT_QUERY_BUF_LEN bytes of adjacent slab
content. KASAN reports the slab-out-of-bounds READ "0 bytes to
the right of the allocated 24-byte region" in the kmalloc-32
cache. Same fix shape as the sibling.
Patches 2 + 4: clamp the channel-iteration upper bound against
the netdetect channels-table length. iwl_mvm_query_set_freqs()
and iwl_mld_set_netdetect_info() iterate the per-match
matching_channels[] bitmap and index a channels[] pointer table
by bit position, without bounding the bit positions against
the table length. The pre-existing caller-side guards compare
popcount to table length, not bit position to table length. The
mvm path iterates over the full 0..55 bit range; the mld path
is accidentally bounded to 0..6 by a bits-vs-bytes confusion
(for_each_set_bit() takes bits, but the call passes sizeof(...)
which is 7 bytes). Both can index past the channels[] allocation
when the user's net-detect configuration has fewer channels than
the relevant bound. The wild-pointer dereference of
channels[j]->center_freq inside the resume work-queue then page-
faults the kernel; a KUnit harness exercising the mvm shape
panics with
Kernel panic - not syncing: Segfault with no mm
and the mld shape panics identically when n_channels < 7. The
mld fix folds the bits-vs-bytes correction together with the
clamp because applying only the bits-correction without the
clamp would widen the OOB exposure from j < 7 to j < 56.
All four bugs require the firmware to produce inputs outside the
implied driver contract: a short response (patches 1, 3), or
matching_channels[] bits set at positions outside the channels-
table bound (patches 2, 4). Well-behaved firmware should not do
either. The patches add defensive validation at a trust boundary;
they are not a claim that current Intel firmware misbehaves.
Reproducer: self-contained KUnit suite that lifts all four buggy
code paths into standalone harnesses (no firmware or hardware
dependency, ~10s on UML). Patched-shape subtests pass cleanly
under the proposed fixes (test_patched_rejects_short,
test_patched_accepts_full, test_set_freqs_patched_clamps,
test_mld_match_info_patched_rejects_short,
test_mld_set_freqs_patched_clamps).
All four patches build clean under x86_64 allmodconfig with no
new warnings; checkpatch --strict reports 0/0/0 on each.
Michael Bommarito (4):
wifi: iwlwifi: mvm: include matches_len in scan-offload-query length
check
wifi: iwlwifi: mvm: clamp set_freqs iteration to n_nd_channels
wifi: iwlwifi: mld: include matches tail in match-info length check
wifi: iwlwifi: mld: clamp netdetect channel iteration to n_channels
drivers/net/wireless/intel/iwlwifi/mld/d3.c | 20 +++++++++++++++-----
drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 10 +++++++---
2 files changed, 22 insertions(+), 8 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/4] wifi: iwlwifi: mvm: include matches_len in scan-offload-query length check
2026-05-15 12:10 [PATCH 0/4] wifi: iwlwifi: harden netdetect resume-path parsing against firmware-controlled inputs (mvm + mld) Michael Bommarito
@ 2026-05-15 12:10 ` Michael Bommarito
2026-05-15 12:10 ` [PATCH 2/4] wifi: iwlwifi: mvm: clamp set_freqs iteration to n_nd_channels Michael Bommarito
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Michael Bommarito @ 2026-05-15 12:10 UTC (permalink / raw)
To: Miri Korenblit
Cc: Johannes Berg, Emmanuel Grumbach, linux-wireless, linux-kernel,
stable
iwl_mvm_netdetect_query_results() validates the firmware response
length against query_len (the fixed-header size of struct
iwl_scan_offload_match_info or iwl_scan_offload_profiles_query_v1)
but immediately follows with:
memcpy(results->matches, query->matches, matches_len);
where matches_len is
sizeof(struct iwl_scan_offload_profile_match[_v1]) *
iwl_umac_scan_get_max_profiles(mvm->fw)
and is not included in the guard. A firmware response of exactly
query_len bytes therefore satisfies the guard yet the memcpy
reads matches_len bytes past the end of the slab-allocated
firmware-response buffer. The worst-case extent depends on the
firmware path:
- v2 layout, SCAN_OFFLOAD_UPDATE_PROFILES_CMD version unknown
or < 3: matches_len = 18 * IWL_SCAN_MAX_PROFILES = 198 bytes.
- v2 layout, command version >= 3:
matches_len = 18 * IWL_SCAN_MAX_PROFILES_V2 = 144 bytes.
- v1 layout: matches_len = 16 * IWL_SCAN_MAX_PROFILES = 176 bytes.
Reproduced under UML+KASAN via a KUnit harness that lifts the
length-validation + memcpy logic into a self-contained test.
With the response sized at the v2 query_len (24 bytes of
match-info header) and the older-firmware max_profiles path,
KASAN reports a slab-out-of-bounds READ of 198 bytes at 0 bytes
to the right of a 24-byte allocation in the kmalloc-32 cache.
Building drivers/net/wireless/intel/iwlwifi/mvm/d3.o under
x86_64 allmodconfig with the fix applied yields no new warnings.
The sibling fix iwl_mvm_nd_match_info_handler() was corrected
by commit 744fabc338e8 ("wifi: iwlwifi: mvm: fix potential
out-of-bounds read in iwl_mvm_nd_match_info_handler()"). The
present function was missed during that audit; apply the same
correction shape.
Cc: stable@vger.kernel.org
Fixes: e4fe5d4b10cd ("iwlwifi: mvm: Support new format of SCAN_OFFLOAD_PROFILES_QUERY_RSP")
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
Assisted-by: Claude:claude-opus-4-7
---
drivers/net/wireless/intel/iwlwifi/mvm/d3.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
index 9a74f60c9185..c17ac62feec3 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c
@@ -2458,7 +2458,7 @@ iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm,
}
len = iwl_rx_packet_payload_len(cmd.resp_pkt);
- if (len < query_len) {
+ if (len < query_len + matches_len) {
IWL_ERR(mvm, "Invalid scan offload profiles query response!\n");
ret = -EIO;
goto out_free_resp;
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/4] wifi: iwlwifi: mvm: clamp set_freqs iteration to n_nd_channels
2026-05-15 12:10 [PATCH 0/4] wifi: iwlwifi: harden netdetect resume-path parsing against firmware-controlled inputs (mvm + mld) Michael Bommarito
2026-05-15 12:10 ` [PATCH 1/4] wifi: iwlwifi: mvm: include matches_len in scan-offload-query length check Michael Bommarito
@ 2026-05-15 12:10 ` Michael Bommarito
2026-05-15 12:10 ` [PATCH 3/4] wifi: iwlwifi: mld: include matches tail in match-info length check Michael Bommarito
2026-05-15 12:11 ` [PATCH 4/4] wifi: iwlwifi: mld: clamp netdetect channel iteration to n_channels Michael Bommarito
3 siblings, 0 replies; 5+ messages in thread
From: Michael Bommarito @ 2026-05-15 12:10 UTC (permalink / raw)
To: Miri Korenblit
Cc: Johannes Berg, Emmanuel Grumbach, linux-wireless, linux-kernel,
stable
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@vger.kernel.org
Fixes: 8ed4e659f34c ("iwlwifi: mvm: add channel information to the netdetect notifications")
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
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
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 3/4] wifi: iwlwifi: mld: include matches tail in match-info length check
2026-05-15 12:10 [PATCH 0/4] wifi: iwlwifi: harden netdetect resume-path parsing against firmware-controlled inputs (mvm + mld) Michael Bommarito
2026-05-15 12:10 ` [PATCH 1/4] wifi: iwlwifi: mvm: include matches_len in scan-offload-query length check Michael Bommarito
2026-05-15 12:10 ` [PATCH 2/4] wifi: iwlwifi: mvm: clamp set_freqs iteration to n_nd_channels Michael Bommarito
@ 2026-05-15 12:10 ` Michael Bommarito
2026-05-15 12:11 ` [PATCH 4/4] wifi: iwlwifi: mld: clamp netdetect channel iteration to n_channels Michael Bommarito
3 siblings, 0 replies; 5+ messages in thread
From: Michael Bommarito @ 2026-05-15 12:10 UTC (permalink / raw)
To: Miri Korenblit
Cc: Johannes Berg, Emmanuel Grumbach, linux-wireless, linux-kernel,
stable
iwl_mld_netdetect_match_info_handler() validates the firmware
notification length against sizeof(*notif) (the fixed-header
size of struct iwl_scan_offload_match_info, 24 bytes) but then
immediately memcpys NETDETECT_QUERY_BUF_LEN bytes from
notif->matches:
if (IWL_FW_CHECK(mld, len < sizeof(*notif),
"Invalid scan offload match notif of length: %d\n",
len))
return true;
...
if (results->matched_profiles)
memcpy(results->matches, notif->matches,
NETDETECT_QUERY_BUF_LEN);
NETDETECT_QUERY_BUF_LEN is
(sizeof(struct iwl_scan_offload_profile_match) *
IWL_SCAN_MAX_PROFILES_V2)
= 18 * 8 = 144 bytes
so a firmware-emitted notif sized at exactly sizeof(*notif)
(24 bytes) satisfies the guard yet the memcpy reads 144 bytes
past the slab-allocated notification buffer.
Reproduced under UML+KASAN via a KUnit harness that lifts the
length-validation + memcpy logic into a self-contained test.
KASAN reports
BUG: KASAN: slab-out-of-bounds in mld_match_info_buggy.constprop.0
Read of size 144 at addr ...
Building drivers/net/wireless/intel/iwlwifi/mld/d3.o under
x86_64 allmodconfig with the fix applied yields no new warnings.
This is the same bug shape as the previously fixed sibling
commit 744fabc338e8 ("wifi: iwlwifi: mvm: fix potential
out-of-bounds read in iwl_mvm_nd_match_info_handler()") applied
to the mvm peer function. The mld driver was added in February
2025 and inherited the same length-check miss; apply the same
correction shape.
Cc: stable@vger.kernel.org
Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver")
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
Assisted-by: Claude:claude-opus-4-7
---
drivers/net/wireless/intel/iwlwifi/mld/d3.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c
index ef98efc8fb1b..e89ec531cb06 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c
@@ -1128,7 +1128,7 @@ iwl_mld_netdetect_match_info_handler(struct iwl_mld *mld,
mld->netdetect))
return true;
- if (IWL_FW_CHECK(mld, len < sizeof(*notif),
+ if (IWL_FW_CHECK(mld, len < sizeof(*notif) + NETDETECT_QUERY_BUF_LEN,
"Invalid scan offload match notif of length: %d\n",
len))
return true;
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 4/4] wifi: iwlwifi: mld: clamp netdetect channel iteration to n_channels
2026-05-15 12:10 [PATCH 0/4] wifi: iwlwifi: harden netdetect resume-path parsing against firmware-controlled inputs (mvm + mld) Michael Bommarito
` (2 preceding siblings ...)
2026-05-15 12:10 ` [PATCH 3/4] wifi: iwlwifi: mld: include matches tail in match-info length check Michael Bommarito
@ 2026-05-15 12:11 ` Michael Bommarito
3 siblings, 0 replies; 5+ messages in thread
From: Michael Bommarito @ 2026-05-15 12:11 UTC (permalink / raw)
To: Miri Korenblit
Cc: Johannes Berg, Emmanuel Grumbach, linux-wireless, linux-kernel,
stable
iwl_mld_set_netdetect_info() walks the per-match matching_channels[]
bitmap and emits one center_freq per set bit by indexing
netdetect_cfg->channels[]:
for_each_set_bit(j,
(unsigned long *)&matches[i].matching_channels[0],
sizeof(matches[i].matching_channels)) {
match->channels[match->n_channels] =
netdetect_cfg->channels[j]->center_freq;
match->n_channels++;
}
Two problems here.
First, the third argument to for_each_set_bit(bit, addr, size) is
the number of BITS to walk, not bytes.
sizeof(matches[i].matching_channels) is
SCAN_OFFLOAD_MATCHING_CHANNELS_LEN = 7 BYTES, so the macro only
visits j = 0..6 and silently misses bits 7..55 of the 56-bit
bitmap. This is a functional defect (per-match channel reporting
is truncated to the first 7 entries of the bitmap).
Second, the loop body indexes netdetect_cfg->channels[j] without
bounding j against netdetect_cfg->n_channels. netdetect_cfg
->channels is a kmemdup()'ed array of pointers sized at exactly
n_channels entries (the user's WoWLAN net-detect channel list).
If n_channels < 7 (a 2.4 GHz only configuration, or a small saved-
SSID channel allowlist) and the firmware sets a match bit at any
position in [n_channels, 6], the indexed load reads past the end
of the allocation, and ->center_freq then dereferences whatever
that wild pointer fetched.
Reproduced under UML+KASAN via a KUnit harness that lifts the
iteration logic. With netdetect_cfg->channels sized at 5 entries
and matching_channels bit 5 set, the kernel panics on the wild
deref:
Kernel panic - not syncing: Segfault with no mm
RIP: 0033:mld_set_freqs_buggy.constprop.0+0x116/0x1c2
(The selector 0x0033 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/mld/d3.o under
x86_64 allmodconfig with the fix applied yields no new warnings.
Rewrite the iteration as an explicit indexed loop with an upper
bound of min(bitmap-width-in-bits, n_channels). This addresses
both issues in one step: bits-correct iteration over the bitmap,
and a hard clamp against the channels-table length. Address the
two together because applying only the bits-correct iteration
without the clamp would widen the OOB exposure from j < 7 to
j < 56.
A short comment is added because the clamp's purpose (avoiding an
OOB pointer fetch from netdetect_cfg->channels) is not obvious
from the expression alone, and a future reader could otherwise
"simplify" the bound back to the underlying constant.
Cc: stable@vger.kernel.org
Fixes: d1e879ec600f ("wifi: iwlwifi: add iwlmld sub-driver")
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
Assisted-by: Claude:claude-opus-4-7
---
drivers/net/wireless/intel/iwlwifi/mld/d3.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c
index e89ec531cb06..51abf414bb1e 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/d3.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c
@@ -1165,7 +1165,7 @@ iwl_mld_set_netdetect_info(struct iwl_mld *mld,
for_each_set_bit(i, &matched_profiles, netdetect_cfg->n_match_sets) {
struct cfg80211_wowlan_nd_match *match;
- int idx, j, n_channels = 0;
+ int idx, j, max, n_channels = 0;
struct iwl_scan_offload_profile_match *matches =
(void *)netdetect_res->matches;
@@ -1192,9 +1192,19 @@ iwl_mld_set_netdetect_info(struct iwl_mld *mld,
if (netdetect_cfg->n_channels < n_channels)
continue;
- for_each_set_bit(j,
- (unsigned long *)&matches[i].matching_channels[0],
- sizeof(matches[i].matching_channels)) {
+ /* Clamp bit-index iteration to the channels table length:
+ * a firmware-set bit past n_channels would otherwise index
+ * past the kmemdup'd netdetect_cfg->channels[] allocation.
+ */
+ max = min_t(int, BITS_PER_BYTE *
+ sizeof(matches[i].matching_channels),
+ netdetect_cfg->n_channels);
+
+ for (j = 0; j < max; j++) {
+ if (!(matches[i].matching_channels[j / BITS_PER_BYTE] &
+ BIT(j % BITS_PER_BYTE)))
+ continue;
+
match->channels[match->n_channels] =
netdetect_cfg->channels[j]->center_freq;
match->n_channels++;
--
2.53.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-05-15 12:12 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-15 12:10 [PATCH 0/4] wifi: iwlwifi: harden netdetect resume-path parsing against firmware-controlled inputs (mvm + mld) Michael Bommarito
2026-05-15 12:10 ` [PATCH 1/4] wifi: iwlwifi: mvm: include matches_len in scan-offload-query length check Michael Bommarito
2026-05-15 12:10 ` [PATCH 2/4] wifi: iwlwifi: mvm: clamp set_freqs iteration to n_nd_channels Michael Bommarito
2026-05-15 12:10 ` [PATCH 3/4] wifi: iwlwifi: mld: include matches tail in match-info length check Michael Bommarito
2026-05-15 12:11 ` [PATCH 4/4] wifi: iwlwifi: mld: clamp netdetect channel iteration to n_channels Michael Bommarito
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox