* [PATCH RESEND rtw-next v2 1/2] wifi: rtw88: 8822c: fix out-of-bounds access in cck_pd_set
2026-11-08 18:28 [PATCH RESEND rtw-next v2 0/2] wifi: rtw88: 8822c: check register values in cck_pd_set Zeshan Ahmed Nobin
@ 2026-11-08 18:28 ` Zeshan Ahmed Nobin
2026-11-09 6:41 ` Luka Gejak
2026-11-08 18:28 ` [PATCH RESEND rtw-next v2 2/2] wifi: rtw88: 8822c: ignore nrx values without CCK PD registers Zeshan Ahmed Nobin
1 sibling, 1 reply; 5+ messages in thread
From: Zeshan Ahmed Nobin @ 2026-11-08 18:28 UTC (permalink / raw)
To: linux-wireless; +Cc: linux-kernel, Ping-Ke Shih, Luka Gejak, Kalle Valo
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:
<TASK>
__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 <zeshanahmednobin@gmail.com>
---
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
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH RESEND rtw-next v2 2/2] wifi: rtw88: 8822c: ignore nrx values without CCK PD registers
2026-11-08 18:28 [PATCH RESEND rtw-next v2 0/2] wifi: rtw88: 8822c: check register values in cck_pd_set Zeshan Ahmed Nobin
2026-11-08 18:28 ` [PATCH RESEND rtw-next v2 1/2] wifi: rtw88: 8822c: fix out-of-bounds access " Zeshan Ahmed Nobin
@ 2026-11-08 18:28 ` Zeshan Ahmed Nobin
2026-11-09 6:43 ` Luka Gejak
1 sibling, 1 reply; 5+ messages in thread
From: Zeshan Ahmed Nobin @ 2026-11-08 18:28 UTC (permalink / raw)
To: linux-wireless; +Cc: linux-kernel, Ping-Ke Shih, Luka Gejak, Kalle Valo
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 USB, a failed
read returns whatever an earlier read left in the buffer, so bw can be
0 or 1 while nrx is 2 or 3. 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; bw
has already been checked at that point.
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 <luka.gejak@linux.dev>
Closes: https://lore.kernel.org/linux-wireless/f6a7d3272fe7635c6a83698dfab5d85500437ca6@linux.dev/
Fixes: 479c4ee931a6 ("rtw88: add dynamic cck pd mechanism")
Signed-off-by: Zeshan Ahmed Nobin <zeshanahmednobin@gmail.com>
---
Notes:
Testing, only on an RTL8822CE (PCIe). There a failed read gives all
ones, so bw is 3 as well and patch 1 returns first. A debug-only change
forced nrx to 2 on a working chip instead:
- 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 with nrx 2,
and there was no UBSAN report.
- With the new check skipped, rtw8822c_phy_cck_pd_set_reg() ran with
nrx 2, and UBSAN reported "shift exponent 64 is too large for 32-bit
type 'unsigned int'" in rtw_read32_mask() and rtw_write32_mask().
- Not tested on USB hardware.
- Build-tested on rtw-next (dc16902cad9c) 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 6343568285bc..acd192684bb1 100644
--- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c
+++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c
@@ -4413,6 +4413,14 @@ static void rtw8822c_phy_cck_pd_set(struct rtw_dev *rtwdev, u8 new_lvl)
if (bw >= ARRAY_SIZE(dm_info->cck_pd_lv))
return;
+ /*
+ * 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
^ permalink raw reply [flat|nested] 5+ messages in thread