From: Bitterblue Smith <rtl8821cerfe2@gmail.com>
To: Ping-Ke Shih <pkshih@realtek.com>, LB F <goainwo@gmail.com>
Cc: "linux-wireless@vger.kernel.org" <linux-wireless@vger.kernel.org>,
"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>
Subject: Re: [BUG] wifi: rtw88: Hard system freeze on RTL8821CE when power_save is enabled (LPS/ASPM conflict)
Date: Sun, 26 Apr 2026 23:38:17 +0300 [thread overview]
Message-ID: <faa215f1-ac2c-4072-9603-4baca1d5e07b@gmail.com> (raw)
In-Reply-To: <5f9003ca-3bfc-45aa-bf0e-35e9c991629d@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 965 bytes --]
On 08/04/2026 17:26, Bitterblue Smith wrote:
> On 31/03/2026 03:32, Ping-Ke Shih wrote:
>>
>> LB F <goainwo@gmail.com> wrote:
>>>
>>> Could you advise on how to investigate this further? For example:
>>>
>>> - Is there a debug flag or register dump we could capture right
>>> before the first corrupted frame in a burst?
>>> - Would it help to log C2H (chip-to-host) traffic around the
>>> time of these events?
>>
>> I have never heard about this symptom from internal, so no clear
>> idea for that. Sorry.
>>
>>>
>>> I am ready to run any specific tests you need. In the meantime,
>>> I agree that filtering by DRV_INFO_SIZE is the right practical
>>> solution, and I'm waiting for your official patch to test locally.
>>
>> As malformed frames happen randomly, more validations like
>> DRV_INFO_SIZE are needed. I think Bitterblue is working on the
>> test patch. :)
>>
>> Ping-Ke
>>
>
> Yes, I am preparing a patch now.
Here is the patch, finally.
[-- Attachment #2: 0001-wifi-rtw88-Add-more-validation-for-the-RX-descriptor.patch --]
[-- Type: text/x-patch, Size: 7468 bytes --]
From 9612876dfd8747060ab25d590d5c172def60922f Mon Sep 17 00:00:00 2001
From: Bitterblue Smith <rtl8821cerfe2@gmail.com>
Date: Sun, 26 Apr 2026 21:15:51 +0300
Subject: [PATCH] wifi: rtw88: Add more validation for the RX descriptor
Some RTL8821CE cards can return frames with corrupted RX descriptor,
causing warnings and crashes if they are passed to the upper layers.
The PHY status size field is 4 bits wide, but in rtw88 its value should
only be 0 or 4. Checking this catches most of the corrupt frames.
If a PHY status is present, the PHY status size should not be 0.
The frame size should not be less than or equal to 4 and should not
exceed 11454.
Discard the frame if any of these check fails.
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221286
Reported-by: Oleksandr Havrylov <goainwo@gmail.com>
Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
---
drivers/net/wireless/realtek/rtw88/pci.c | 16 +++++++-----
drivers/net/wireless/realtek/rtw88/rx.c | 32 ++++++++++++++++++++---
drivers/net/wireless/realtek/rtw88/rx.h | 6 ++---
drivers/net/wireless/realtek/rtw88/sdio.c | 8 +++++-
drivers/net/wireless/realtek/rtw88/usb.c | 9 ++++---
5 files changed, 54 insertions(+), 17 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c
index bba370ad510c..344f953f0a10 100644
--- a/drivers/net/wireless/realtek/rtw88/pci.c
+++ b/drivers/net/wireless/realtek/rtw88/pci.c
@@ -1042,20 +1042,21 @@ static int rtw_pci_get_hw_rx_ring_nr(struct rtw_dev *rtwdev,
static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci,
u8 hw_queue, u32 limit)
{
+ struct rtw_pci_rx_ring *ring = &rtwpci->rx_rings[RTW_RX_QUEUE_MPDU];
const struct rtw_chip_info *chip = rtwdev->chip;
struct napi_struct *napi = &rtwpci->napi;
- struct rtw_pci_rx_ring *ring = &rtwpci->rx_rings[RTW_RX_QUEUE_MPDU];
- struct rtw_rx_pkt_stat pkt_stat;
+ u32 pkt_desc_sz = chip->rx_pkt_desc_sz;
+ u32 buf_desc_sz = chip->rx_buf_desc_sz;
struct ieee80211_rx_status rx_status;
+ struct rtw_rx_pkt_stat pkt_stat;
struct sk_buff *skb, *new;
u32 cur_rp = ring->r.rp;
u32 count, rx_done = 0;
u32 pkt_offset;
- u32 pkt_desc_sz = chip->rx_pkt_desc_sz;
- u32 buf_desc_sz = chip->rx_buf_desc_sz;
+ dma_addr_t dma;
u32 new_len;
u8 *rx_desc;
- dma_addr_t dma;
+ int ret;
count = rtw_pci_get_hw_rx_ring_nr(rtwdev, rtwpci);
count = min(count, limit);
@@ -1067,7 +1068,10 @@ static u32 rtw_pci_rx_napi(struct rtw_dev *rtwdev, struct rtw_pci *rtwpci,
dma_sync_single_for_cpu(rtwdev->dev, dma, RTK_PCI_RX_BUF_SIZE,
DMA_FROM_DEVICE);
rx_desc = skb->data;
- rtw_rx_query_rx_desc(rtwdev, rx_desc, &pkt_stat, &rx_status);
+ ret = rtw_rx_query_rx_desc(rtwdev, rx_desc,
+ &pkt_stat, &rx_status);
+ if (ret)
+ goto next_rp;
/* offset from rx_desc to payload */
pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz +
diff --git a/drivers/net/wireless/realtek/rtw88/rx.c b/drivers/net/wireless/realtek/rtw88/rx.c
index d9e11343d498..3343395ef423 100644
--- a/drivers/net/wireless/realtek/rtw88/rx.c
+++ b/drivers/net/wireless/realtek/rtw88/rx.c
@@ -3,6 +3,7 @@
*/
#include "main.h"
+#include "mac.h"
#include "rx.h"
#include "ps.h"
#include "debug.h"
@@ -261,9 +262,9 @@ static void rtw_rx_fill_rx_status(struct rtw_dev *rtwdev,
}
}
-void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8,
- struct rtw_rx_pkt_stat *pkt_stat,
- struct ieee80211_rx_status *rx_status)
+int rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8,
+ struct rtw_rx_pkt_stat *pkt_stat,
+ struct ieee80211_rx_status *rx_status)
{
u32 desc_sz = rtwdev->chip->rx_pkt_desc_sz;
struct rtw_rx_desc *rx_desc = rx_desc8;
@@ -303,12 +304,33 @@ void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8,
pkt_stat->bw = RTW_CHANNEL_WIDTH_20;
}
+ if (unlikely(pkt_stat->drv_info_sz &&
+ pkt_stat->drv_info_sz != PHY_STATUS_SIZE)) {
+ pr_err_once("drv_info_sz %d\n", pkt_stat->drv_info_sz);
+ return -EINVAL;
+ }
+
+ if (unlikely(pkt_stat->phy_status && !pkt_stat->drv_info_sz)) {
+ pr_err_once("phy_status but no drv_info_sz\n");
+ return -EINVAL;
+ }
+
+ if (unlikely(pkt_stat->pkt_len > IEEE80211_MAX_MPDU_LEN_VHT_11454)) {
+ pr_err_once("pkt_len %d\n", pkt_stat->pkt_len);
+ return -EINVAL;
+ }
+
/* drv_info_sz is in unit of 8-bytes */
pkt_stat->drv_info_sz *= 8;
/* c2h cmd pkt's rx/phy status is not interested */
if (pkt_stat->is_c2h)
- return;
+ return 0;
+
+ if (unlikely(pkt_stat->pkt_len <= FCS_LEN)) {
+ pr_err_once("not c2h pkt_len %d\n", pkt_stat->pkt_len);
+ return -EINVAL;
+ }
phy_status = rx_desc8 + desc_sz + pkt_stat->shift;
hdr = phy_status + pkt_stat->drv_info_sz;
@@ -318,5 +340,7 @@ void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8,
rtwdev->chip->ops->query_phy_status(rtwdev, phy_status, pkt_stat);
rtw_rx_fill_rx_status(rtwdev, pkt_stat, hdr, rx_status);
+
+ return 0;
}
EXPORT_SYMBOL(rtw_rx_query_rx_desc);
diff --git a/drivers/net/wireless/realtek/rtw88/rx.h b/drivers/net/wireless/realtek/rtw88/rx.h
index 6b7dee245c0a..74359f641c76 100644
--- a/drivers/net/wireless/realtek/rtw88/rx.h
+++ b/drivers/net/wireless/realtek/rtw88/rx.h
@@ -45,9 +45,9 @@ struct rtw_rx_desc {
void rtw_rx_stats(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
struct sk_buff *skb);
-void rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8,
- struct rtw_rx_pkt_stat *pkt_stat,
- struct ieee80211_rx_status *rx_status);
+int rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8,
+ struct rtw_rx_pkt_stat *pkt_stat,
+ struct ieee80211_rx_status *rx_status);
void rtw_update_rx_freq_from_ie(struct rtw_dev *rtwdev, struct sk_buff *skb,
struct ieee80211_rx_status *rx_status,
struct rtw_rx_pkt_stat *pkt_stat);
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
index 1318e94f8524..5b40d74b16ee 100644
--- a/drivers/net/wireless/realtek/rtw88/sdio.c
+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
@@ -995,7 +995,13 @@ static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
while (true) {
rx_desc = skb->data;
- rtw_rx_query_rx_desc(rtwdev, rx_desc, &pkt_stat, &rx_status);
+ ret = rtw_rx_query_rx_desc(rtwdev, rx_desc,
+ &pkt_stat, &rx_status);
+ if (ret) {
+ dev_kfree_skb_any(skb);
+ return;
+ }
+
pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz +
pkt_stat.shift;
diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c
index 718940ebba31..6dd8ffedab9a 100644
--- a/drivers/net/wireless/realtek/rtw88/usb.c
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
@@ -610,8 +610,8 @@ static void rtw_usb_rx_handler(struct work_struct *work)
u32 max_skb_len = pkt_desc_sz + PHY_STATUS_SIZE * 8 +
IEEE80211_MAX_MPDU_LEN_VHT_11454;
u32 pkt_offset, next_pkt, skb_len;
+ int limit, ret;
u8 *rx_desc;
- int limit;
for (limit = 0; limit < 200; limit++) {
rx_skb = skb_dequeue(&rtwusb->rx_queue);
@@ -627,8 +627,11 @@ static void rtw_usb_rx_handler(struct work_struct *work)
rx_desc = rx_skb->data;
do {
- rtw_rx_query_rx_desc(rtwdev, rx_desc, &pkt_stat,
- &rx_status);
+ ret = rtw_rx_query_rx_desc(rtwdev, rx_desc,
+ &pkt_stat, &rx_status);
+ if (ret)
+ break;
+
pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz +
pkt_stat.shift;
--
2.53.0
next prev parent reply other threads:[~2026-04-26 20:38 UTC|newest]
Thread overview: 58+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-09 21:48 [BUG] wifi: rtw88: Hard system freeze on RTL8821CE when power_save is enabled (LPS/ASPM conflict) LB F
2026-03-10 2:02 ` Ping-Ke Shih
2026-03-10 11:01 ` LB F
2026-03-10 15:12 ` LB F
2026-03-11 2:20 ` Ping-Ke Shih
2026-03-11 2:15 ` Ping-Ke Shih
2026-03-11 2:22 ` Ping-Ke Shih
2026-03-11 11:00 ` LB F
2026-03-11 15:22 ` LB F
2026-03-12 1:56 ` Ping-Ke Shih
2026-03-12 21:42 ` LB F
2026-03-13 0:03 ` LB F
2026-03-13 0:29 ` LB F
2026-03-14 10:52 ` LB F
2026-03-14 12:39 ` LB F
2026-03-15 0:24 ` LB F
2026-03-16 2:55 ` Ping-Ke Shih
2026-03-16 20:27 ` LB F
2026-03-17 1:28 ` Ping-Ke Shih
2026-03-18 0:00 ` LB F
2026-03-18 0:58 ` Ping-Ke Shih
2026-03-18 23:55 ` LB F
2026-03-19 0:22 ` LB F
2026-03-19 0:49 ` Ping-Ke Shih
2026-03-19 1:24 ` Ping-Ke Shih
2026-03-19 23:58 ` LB F
2026-03-20 0:41 ` LB F
2026-03-20 1:00 ` Ping-Ke Shih
2026-03-20 1:19 ` LB F
2026-03-20 2:02 ` Ping-Ke Shih
2026-03-21 12:07 ` LB F
2026-03-23 2:01 ` Ping-Ke Shih
2026-03-25 20:38 ` LB F
2026-03-26 23:52 ` LB F
2026-03-27 10:52 ` Bitterblue Smith
2026-03-28 11:41 ` LB F
2026-03-28 13:07 ` Bitterblue Smith
2026-03-28 13:40 ` LB F
2026-03-28 18:52 ` Bitterblue Smith
2026-03-28 20:59 ` LB F
2026-03-28 21:31 ` LB F
2026-03-28 21:53 ` LB F
2026-03-30 1:23 ` Ping-Ke Shih
2026-03-30 11:34 ` LB F
2026-03-31 0:32 ` Ping-Ke Shih
2026-04-03 21:47 ` LB F
2026-04-04 10:44 ` LB F
2026-04-06 12:41 ` LB F
2026-04-08 14:26 ` Bitterblue Smith
2026-04-26 20:38 ` Bitterblue Smith [this message]
2026-04-26 23:17 ` LB F
2026-04-27 0:26 ` LB F
2026-04-27 10:08 ` LB F
2026-04-27 21:19 ` LB F
2026-04-27 21:30 ` Bitterblue Smith
2026-04-27 21:48 ` LB F
2026-04-27 22:25 ` LB F
2026-03-16 2:50 ` Ping-Ke Shih
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=faa215f1-ac2c-4072-9603-4baca1d5e07b@gmail.com \
--to=rtl8821cerfe2@gmail.com \
--cc=goainwo@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-wireless@vger.kernel.org \
--cc=pkshih@realtek.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox