rtw8822c_phy_cck_pd_set() reads the bandwidth from a 2-bit register field and uses it as the first index into dm_info->cck_pd_lv[], which only has entries for 20 and 40 MHz. When the chip stops responding on the PCIe bus, register reads return all ones and bw becomes 3. The function then reads cck_pd_lv[3][nrx], past the end of the array. If that byte differs from new_lvl, it calls rtw8822c_phy_cck_pd_set_reg(), which rejects bw with WARN_ON(), and then writes new_lvl to cck_pd_lv[3][nrx] anyway. UBSAN reports the first of these accesses as: UBSAN: array-index-out-of-bounds in .../rtw88/rtw8822c.c:4407:21 index 3 is out of range for type 'u8 [2][4]' Workqueue: phy0 rtw_watch_dog_work [rtw88_core] Call Trace: __ubsan_handle_out_of_bounds+0xdd/0x120 rtw8822c_phy_cck_pd_set+0x33c/0x510 [rtw88_8822c] rtw_phy_dynamic_mechanism+0x310/0x330 [rtw88_core] rtw_watch_dog_work+0x217/0x330 [rtw88_core] Return early when bw is out of range. The chip is already unusable at that point; this only stops the driver from corrupting its own state. The fix and this message were drafted with an AI coding assistant (Claude Opus 5.5 by Anthropic), then reviewed and tested by the submitter. Fixes: 479c4ee931a6 ("rtw88: add dynamic cck pd mechanism") Cc: stable@vger.kernel.org Signed-off-by: Zeshan Ahmed Nobin --- Notes: Testing, on an RTL8822CE in an ASUS TUF Gaming A15 (FA506IU) whose chip intermittently drops off the PCIe bus: - Without this patch, on Ubuntu's 6.8.0-147 and 7.0.0-42 kernels, 22 of the 23 boots with a chip failure logged three UBSAN reports for the reads, the WARN_ON() and one report for the write; the other logged neither. Boots without a failure logged neither. The trace above is from 7.0.0-42, whose line numbers are three lower than mainline here. - With v1 applied to 6.8.0-147, 5 boots with chip failures logged no UBSAN report and no WARN_ON(). - v2 on 6.8.0-147, with a debug-only change that forces bw to 3 on a working chip: in 12 s the function ran 6 times and returned early each time, with no UBSAN report and no WARN_ON(). With the new check also skipped, the same input brought back the UBSAN reports for the three reads and the write and the WARN_ON(), plus two more for pd_lvl[cur_lvl] and cs_lvl[cur_lvl] ("index 36 is out of range for type 's8 [5]'"). - Build-tested on rtw-next (dc16902cad9c) with W=1: no warnings. drivers/net/wireless/realtek/rtw88/rtw8822c.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c index 09315d23e2c4..6343568285bc 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c @@ -4406,6 +4406,13 @@ static void rtw8822c_phy_cck_pd_set(struct rtw_dev *rtwdev, u8 new_lvl) nrx = (u8)rtw_read32_mask(rtwdev, 0x1a2c, 0x60000); bw = (u8)rtw_read32_mask(rtwdev, 0x9b0, 0xc); + /* + * cck_pd_lv[] only has entries for 20 and 40 MHz. A chip that stops + * responding on the PCIe bus reads back as all ones, giving bw = 3. + */ + if (bw >= ARRAY_SIZE(dm_info->cck_pd_lv)) + return; + rtw_dbg(rtwdev, RTW_DBG_PHY, "lv: (%d) -> (%d) bw=%d nr=%d cck_fa_avg=%d\n", dm_info->cck_pd_lv[bw][nrx], new_lvl, bw, nrx, dm_info->cck_fa_avg); -- 2.43.0