[PATCH wireless] wifi: mt76: mt7921: fix array-index-out-of-bounds in mt7921_load_clc()
Mikhail Gavrilov
mikhail.v.gavrilov at gmail.com
Sat Aug 15 03:33:12 PDT 2026
mt7921_load_clc() walks the CLC region of the firmware image and uses
clc->idx, a value taken straight from the blob, as an index into
phy->clc[] without validating it. The array has MT792x_CLC_MAX_NUM (3)
entries, so a firmware image carrying a section with a larger index
overruns it.
linux-firmware 20260810 does exactly that. Dumping the CLC region of
mediatek/WIFI_RAM_CODE_MT7922_1.bin before and after the update:
20260622, region len 366448:
idx=0 ver=1 nr_country=255 type=0 len=179384
idx=0 ver=1 nr_country=255 type=1 len=187064
20260810, region len 475488:
idx=0 ver=1 nr_country=255 type=0 len=179384
idx=0 ver=1 nr_country=255 type=1 len=187054
idx=3 ver=1 nr_country=0 type=0 len=54520
idx=3 ver=1 nr_country=0 type=1 len=54530
and UBSAN reports the overrun on every probe:
UBSAN: array-index-out-of-bounds in
drivers/net/wireless/mediatek/mt76/mt7921/mcu.c:471:15
index 3 is out of range for type 'void *[3]'
CPU: 19 UID: 0 PID: 261 Comm: kworker/19:1 Tainted: G U 7.2.0-rc7-2f1baf1fc892-with-fixes-v1+ #125 PREEMPT(lazy)
Hardware name: ASUS System Product Name/ROG STRIX B650E-I GAMING WIFI, BIOS 3854 04/03/2026
Workqueue: events mt7921_init_work [mt7921_common]
Call Trace:
<TASK>
dump_stack_lvl+0x84/0xd0
ubsan_epilogue+0x5/0x2b
__ubsan_handle_out_of_bounds.cold+0x4e/0x58
mt7921_load_clc+0x826/0xb80 [mt7921_common]
mt7921_run_firmware+0x113/0x180 [mt7921_common]
mt7921e_mcu_init+0xba/0x18d [mt7921e]
mt7921_init_work+0xdb/0x3f0 [mt7921_common]
process_one_work+0x901/0x1640
worker_thread+0x601/0xff0
kthread+0x36e/0x470
ret_from_fork+0x5bf/0x910
ret_from_fork_asm+0x1a/0x30
</TASK>
Both idx=3 sections are read out of bounds; UBSAN deduplicates by
source location, so only one report appears. Booting the same kernel
with linux-firmware 20260622 is clean, so the overrun is only reachable
with the newer blob, but the missing check itself predates it.
phy->clc[3] aliases phy->chip_cap, the u64 that follows the array in
struct mt792x_phy. The report above is the read in the "do not init buf
again" test. mt7921_mcu_get_nic_capability() runs before
mt7921_load_clc() and fills chip_cap in from MT_NIC_CAP_CHIP_CAP, so on
this device it is non-zero, the loop takes the continue path and no
store happens. On a device whose firmware does not report that tag
chip_cap stays zero, and the following devm_kmemdup() stores a heap
pointer into it instead, enabling whatever MT792x_CHIP_CAP_* bits that
pointer happens to have set.
Skip CLC sections whose index the driver does not know about, so that
they are ignored deliberately rather than by accident. With the
firmware above this is not a functional change: the idx=3 sections are
dropped either way. Use continue rather than break so that known
sections following an unknown one are still parsed. mt7925_load_clc()
has had an equivalent check since commit 9679ca7326e5 ("wifi: mt76:
mt7925: fix a potential array-index-out-of-bounds issue for clc").
Fixes: 23bdc5d8cadf ("wifi: mt76: mt7921: introduce Country Location Control support")
Cc: stable at vger.kernel.org
Signed-off-by: Mikhail Gavrilov <mikhail.v.gavrilov at gmail.com>
---
ARRAY_SIZE(phy->clc) is used rather than a named constant on purpose.
mt7921.h still carries
enum {
MT7921_CLC_POWER,
MT7921_CLC_CHAN,
MT7921_CLC_MAX_NUM,
};
whose MT7921_CLC_MAX_NUM is 2 and does not match the array, which is
sized by MT792x_CLC_MAX_NUM (3) in mt792x.h. That enum is otherwise
unused except for MT7921_CLC_POWER, which happens to have the same
value as MT792x_CLC_POWER. Removing it is a separate cleanup.
The two idx=3 sections add roughly 109 KB of payload that the driver
now discards explicitly. If mt7921 is supposed to consume them, that
needs a MediaTek patch adding a fourth MT792x_CLC_* entry; this one
only stops the out-of-bounds access.
Tested on an MT7922 (mt7921e) on v7.2-rc7 with linux-firmware 20260810:
the UBSAN report is gone and the regulatory domain is unchanged.
drivers/net/wireless/mediatek/mt76/mt7921/mcu.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
index 25b9437250f7..1b147b492b9f 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
@@ -467,6 +467,9 @@ static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name)
for (offset = 0; offset < len; offset += le32_to_cpu(clc->len)) {
clc = (const struct mt7921_clc *)(clc_base + offset);
+ if (clc->idx >= ARRAY_SIZE(phy->clc))
+ continue;
+
/* do not init buf again if chip reset triggered */
if (phy->clc[clc->idx])
continue;
--
2.55.0
More information about the Linux-mediatek
mailing list