rtw8822c_phy_cck_pd_set() reads nrx, the number of CCK RX paths minus one, from a 2-bit register field. The driver only ever sets it to 0 or 1, for one or two RX paths (see rtw8822c_config_cck_rx_path()), and rtw8822c_cck_pd_reg[] only has registers for those two values; its entries for nrx 2 and 3 are all zero. A device that stops responding can return any value. On PCIe, register reads then return all ones, so nrx is 3. On USB, a failed read returns whatever an earlier read left in the buffer. bw comes from rtwdev->hal.current_band_width and stays valid. When the level then changes, rtw8822c_phy_cck_pd_set_reg() gets past its WARN_ON(), which only checks nrx against RTW_RF_PATH_MAX, and calls rtw_read32_mask() and rtw_write32_mask() on register 0x0 with a zero mask. Both compute __ffs(0), which is undefined, and the write puts back whatever was read from register 0x0. Return early when rtw8822c_cck_pd_reg[] has no registers for nrx. 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. Reported-by: Luka Gejak Closes: https://lore.kernel.org/linux-wireless/f6a7d3272fe7635c6a83698dfab5d85500437ca6@linux.dev/ Fixes: 479c4ee931a6 ("rtw88: add dynamic cck pd mechanism") Cc: stable@vger.kernel.org Reviewed-by: Luka Gejak Signed-off-by: Zeshan Ahmed Nobin --- Notes: Testing, on the same RTL8822CE (PCIe) with both patches on 6.8.0-147, and a debug-only change that forces nrx to 3 on a working chip at 40 MHz on 2.4 GHz: - With this patch, in 12 s the function ran 6 times and returned early each time. rtw8822c_phy_cck_pd_set_reg() was never called, and there was no UBSAN report. - With the new check also skipped, rtw8822c_phy_cck_pd_set_reg() ran with nrx 3, and UBSAN reported "shift exponent 64 is too large for 32-bit type 'unsigned int'" in rtw_read32_mask() and rtw_write32_mask(). There was no WARN_ON(), as bw was valid. That is the path a PCIe chip that stops responding takes with patch 1 alone. - In the second of the two chip failures described in patch 1, a debug-only counter showed that rtw8822c_phy_cck_pd_set_reg() was never called with nrx 2 or 3. - Not tested on USB hardware. - Build-tested on rtw-next (04c998418722) with W=1: no warnings. drivers/net/wireless/realtek/rtw88/rtw8822c.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c index df7df76785e4..1eb1a9656886 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c @@ -4406,6 +4406,14 @@ static void rtw8822c_phy_cck_pd_set(struct rtw_dev *rtwdev, u8 new_lvl) nrx = (u8)rtw_read32_mask(rtwdev, 0x1a2c, 0x60000); bw = rtwdev->hal.current_band_width; + /* + * rtw8822c_cck_pd_reg[] only has registers for one and two RX paths + * (nrx 0 and 1); its entries for nrx 2 and 3 are all zero. A device + * that stops responding can still return those values. + */ + if (!rtw8822c_cck_pd_reg[bw][nrx].mask_pd) + 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