All of lore.kernel.org
 help / color / mirror / Atom feed
From: Luka Gejak <luka.gejak@linux.dev>
To: Ping-Ke Shih <pkshih@realtek.com>
Cc: Bob Beckett <bob.beckett@collabora.com>,
	linux-wireless@vger.kernel.org, kernel@collabora.com,
	matthew.leach@collabora.com, Luka Gejak <luka.gejak@linux.dev>
Subject: [PATCH rtw-next] wifi: rtw88: do not trust a claimed decryption
Date: Sat, 17 Oct 2026 10:33:13 +0200	[thread overview]
Message-ID: <20261017083313.56113-1-luka.gejak@linux.dev> (raw)

The RX descriptor says how the CRC and ICV checks on the device's own copy
of the frame went, not that the bytes in our buffer are that frame.  For a
frame that is marked decrypted mac80211 reads the PN out of the CCMP header
and stores it as the key's last PN, rejecting only a lower PN, so a single
frame with a garbage CCMP header makes every genuine frame after it be
dropped as a replay.

Only claim decryption for a frame that passed the CRC and ICV checks, and
only for an AES frame whose CCMP or GCMP header is well formed.  Such a
frame is left to mac80211, which drops it without moving the replay
counter.  Mark a frame that failed the ICV with RX_FLAG_FAILED_FCS_CRC,
the way rtw89 does.

Reported-by: Bob Beckett <bob.beckett@collabora.com>
Closes: https://lore.kernel.org/all/20261016191100.1781056-1-bob.beckett@collabora.com/
Signed-off-by: Luka Gejak <luka.gejak@linux.dev>
---
 drivers/net/wireless/realtek/rtw88/rx.c | 30 +++++++++++++++++++++++--
 1 file changed, 28 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw88/rx.c b/drivers/net/wireless/realtek/rtw88/rx.c
index fde21c840ac0..45ff18fa8c7b 100644
--- a/drivers/net/wireless/realtek/rtw88/rx.c
+++ b/drivers/net/wireless/realtek/rtw88/rx.c
@@ -208,7 +208,7 @@ static void rtw_rx_fill_rx_status(struct rtw_dev *rtwdev,
 	if (rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_SCAN_OFFLOAD) &&
 	    test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
 		rtw_set_rx_freq_by_pktstat(pkt_stat, rx_status);
-	if (pkt_stat->crc_err)
+	if (pkt_stat->crc_err || pkt_stat->icv_err)
 		rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
 	if (pkt_stat->decrypted)
 		rx_status->flag |= RX_FLAG_DECRYPTED;
@@ -264,6 +264,24 @@ static void rtw_rx_fill_rx_status(struct rtw_dev *rtwdev,
 	}
 }
 
+static bool rtw_rx_ccmp_hdr_valid(struct rtw_rx_pkt_stat *pkt_stat,
+				  struct ieee80211_hdr *hdr)
+{
+	u32 hdr_len = ieee80211_hdrlen(hdr->frame_control);
+	u8 *iv;
+
+	if (!ieee80211_has_protected(hdr->frame_control))
+		return true;
+
+	if (pkt_stat->pkt_len < hdr_len + IEEE80211_CCMP_HDR_LEN + FCS_LEN)
+		return false;
+
+	iv = (u8 *)hdr + hdr_len;
+
+	/* reserved octet is zero, key ID octet has ExtIV set */
+	return iv[2] == 0 && (iv[3] & BIT(5));
+}
+
 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)
@@ -285,7 +303,8 @@ int rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8,
 	pkt_stat->shift = le32_get_bits(rx_desc->w0, RTW_RX_DESC_W0_SHIFT);
 	pkt_stat->phy_status = le32_get_bits(rx_desc->w0, RTW_RX_DESC_W0_PHYST);
 	swdec = le32_get_bits(rx_desc->w0, RTW_RX_DESC_W0_SWDEC);
-	pkt_stat->decrypted = !swdec && enc_type != RX_DESC_ENC_NONE;
+	pkt_stat->decrypted = !swdec && enc_type != RX_DESC_ENC_NONE &&
+			      !pkt_stat->crc_err && !pkt_stat->icv_err;
 
 	pkt_stat->cam_id = le32_get_bits(rx_desc->w1, RTW_RX_DESC_W1_MACID);
 
@@ -328,6 +347,13 @@ int rtw_rx_query_rx_desc(struct rtw_dev *rtwdev, void *rx_desc8,
 	if (pkt_stat->phy_status)
 		rtwdev->chip->ops->query_phy_status(rtwdev, phy_status, pkt_stat);
 
+	if (pkt_stat->decrypted && enc_type == RX_DESC_ENC_AES &&
+	    !rtw_rx_ccmp_hdr_valid(pkt_stat, hdr)) {
+		rtw_dbg(rtwdev, RTW_DBG_RX,
+			"AES frame without a usable CCMP header, drop the decryption flag\n");
+		pkt_stat->decrypted = false;
+	}
+
 	rtw_rx_fill_rx_status(rtwdev, pkt_stat, hdr, rx_status);
 
 	return 0;
-- 
2.53.0


                 reply	other threads:[~2026-10-17  8:33 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

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=20261017083313.56113-1-luka.gejak@linux.dev \
    --to=luka.gejak@linux.dev \
    --cc=bob.beckett@collabora.com \
    --cc=kernel@collabora.com \
    --cc=linux-wireless@vger.kernel.org \
    --cc=matthew.leach@collabora.com \
    --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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.