Subject: [PATCH 3/3] wifi: mt76: mt7996: use relay_subbuf_avail() to fix stale fwlog reads

From: Jason Xing <[email-masked]>
Date: Sun, 31 May 2026 11:40:10 +0800

From: Jason Xing <[email-masked]>

relay_reserve() advances buf->offset before the caller writes data.
Since relay_file_read() uses buf->offset as the readable upper bound
of the active sub-buffer, a concurrent reader can observe the
reserved-but-not-yet-written region, resulting in stale data.

  WRITER                          READER (active sub-buf)
  ------                          ------
  relay_reserve(4+L)
    buf->offset += 4+L  ---+
  *(u32*)dest = L           |  (offset already exposes the slot)
                            +--> read(&len, 4)  => L (VALID!)
      <<preempted>>              read(buf, L)   => STALE data X
  memcpy(dest+4, ..., L)
    [payload written - too late]

The userspace reader [1] uses poll() + read(&len, 4) + read(buf, len),
which is racy against the relay_reserve() window described above.

Switch to relay_subbuf_avail() + __relay_write() so the offset only
advances after each chunk is copied.

[1] https://github.com/openwrt/mt76/blob/master/tools/fwlog.c

Fixes: 98686cd21624 ("wifi: mt76: mt7996: add driver for MediaTek Wi-Fi 7 (802.11be) devices")
Signed-off-by: Jason Xing <[email-masked]>
---
 .../wireless/mediatek/mt76/mt7996/debugfs.c   | 25 +++++++++----------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
index 34af800964d1..82f59a7eb508 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c
@@ -914,22 +914,21 @@ mt7996_debugfs_write_fwlog(struct mt7996_dev *dev, const void *hdr, int hdrlen,
 {
 	static DEFINE_SPINLOCK(lock);
 	unsigned long flags;
-	void *dest;
+	u32 rec_len = len;
+
+	if (hdr)
+		rec_len += hdrlen;
 
 	spin_lock_irqsave(&lock, flags);
-	dest = relay_reserve(dev->relay_fwlog, hdrlen + len + 4);
-	if (dest) {
-		*(u32 *)dest = hdrlen + len;
-		dest += 4;
-
-		if (hdrlen) {
-			memcpy(dest, hdr, hdrlen);
-			dest += hdrlen;
-		}
+	if (!relay_subbuf_avail(dev->relay_fwlog, sizeof(rec_len) + rec_len))
+		goto out;
 
-		memcpy(dest, data, len);
-		relay_flush(dev->relay_fwlog);
-	}
+	__relay_write(dev->relay_fwlog, &rec_len, sizeof(rec_len));
+	if (hdr)
+		__relay_write(dev->relay_fwlog, hdr, hdrlen);
+	__relay_write(dev->relay_fwlog, data, len);
+	relay_flush(dev->relay_fwlog);
+out:
 	spin_unlock_irqrestore(&lock, flags);
 }
 
-- 
2.43.7