mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH wireless] wifi: mac80211: protect the mesh RMC with a lock
@ 2026-10-25  3:05 Joas Antonio dos Santos
  0 siblings, 0 replies; only message in thread
From: Joas Antonio dos Santos @ 2026-10-25  3:05 UTC (permalink / raw)
  To: Johannes Berg; +Cc: Felix Fietkau, linux-wireless, linux-kernel

mesh_rmc_check() looks up, unlinks/frees and inserts rmc_entry objects
in the per-interface recent multicast cache without any locking.

Until mesh fast-rx support was added it was only called from the rx
handlers, which run under local->rx_path_lock.  Since then,
ieee80211_invoke_fast_rx() also calls ieee80211_rx_mesh_data() (for
plain frames and for each A-MSDU subframe), and that path does not take
rx_path_lock.  Fast-rx is meant to run in parallel on several CPUs for
drivers that set RX_FLAG_DUP_VALIDATED and USES_RSS, such as ath11k and
ath12k, which also support mesh.  Fast-rx is enabled for authorized
mesh peers as long as the driver does reordering itself and no
unsupported cipher is in use.

A frame reaches mesh_rmc_check() on the fast path when addr1 is our
address and the mesh DA (addr3, or the subframe DA in an A-MSDU) is a
group address.  With two such frames being processed on different CPUs
(or one there and one in the locked slow path) whose sequence numbers
map to the same bucket, both can walk and modify the same hlist at
the same time, and both can unlink and free the same expired or
oldest entry, corrupting the list and double freeing into rm_cache.
The sequence number, and so the bucket, is chosen by the sender.

Add a spinlock to struct mesh_rmc and hold it across the lookup and
the insertion.

Fixes: 3468e1e0c639 ("wifi: mac80211: add mesh fast-rx support")
Signed-off-by: Joas Antonio dos Santos <joasantonio108@gmail.com>
Assisted-by: Yaga Agent HackerSec Claude:claude-opus-5-5
---
Found by code inspection, compile-tested only (W=1 with
CONFIG_MAC80211_MESH=y).  I have not hit the race: it needs a driver
that delivers rx in parallel (ath11k/ath12k with several REO rings),
and I don't have that hardware.  mac80211_hwsim does not do parallel rx.

 net/mac80211/mesh.c | 16 ++++++++++++----
 net/mac80211/mesh.h |  2 ++
 2 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 8f88141253..e3718a2252 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -184,6 +184,7 @@ int mesh_rmc_init(struct ieee80211_sub_if_data *sdata)
 	if (!sdata->u.mesh.rmc)
 		return -ENOMEM;
 	sdata->u.mesh.rmc->idx_mask = RMC_BUCKETS - 1;
+	spin_lock_init(&sdata->u.mesh.rmc->lock);
 	for (i = 0; i < RMC_BUCKETS; i++)
 		INIT_HLIST_HEAD(&sdata->u.mesh.rmc->bucket[i]);
 	return 0;
@@ -229,6 +230,7 @@ int mesh_rmc_check(struct ieee80211_sub_if_data *sdata,
 	struct mesh_rmc *rmc = sdata->u.mesh.rmc;
 	u32 seqnum = 0;
 	int entries = 0;
+	int ret = 0;
 	u8 idx;
 	struct rmc_entry *p;
 	struct hlist_node *n;
@@ -239,6 +241,8 @@ int mesh_rmc_check(struct ieee80211_sub_if_data *sdata,
 	/* Don't care about endianness since only match matters */
 	memcpy(&seqnum, &mesh_hdr->seqnum, sizeof(mesh_hdr->seqnum));
 	idx = le32_to_cpu(mesh_hdr->seqnum) & rmc->idx_mask;
+
+	spin_lock_bh(&rmc->lock);
 	hlist_for_each_entry_safe(p, n, &rmc->bucket[idx], list) {
 		++entries;
 		if (time_after(jiffies, p->exp_time) ||
@@ -246,19 +250,23 @@ int mesh_rmc_check(struct ieee80211_sub_if_data *sdata,
 			hlist_del(&p->list);
 			kmem_cache_free(rm_cache, p);
 			--entries;
-		} else if ((seqnum == p->seqnum) && ether_addr_equal(sa, p->sa))
-			return -1;
+		} else if ((seqnum == p->seqnum) && ether_addr_equal(sa, p->sa)) {
+			ret = -1;
+			goto out;
+		}
 	}
 
 	p = kmem_cache_alloc(rm_cache, GFP_ATOMIC);
 	if (!p)
-		return 0;
+		goto out;
 
 	p->seqnum = seqnum;
 	p->exp_time = jiffies + RMC_TIMEOUT;
 	memcpy(p->sa, sa, ETH_ALEN);
 	hlist_add_head(&p->list, &rmc->bucket[idx]);
-	return 0;
+out:
+	spin_unlock_bh(&rmc->lock);
+	return ret;
 }
 
 int mesh_add_meshconf_ie(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h
index 3f9664e4e0..27f12ebc28 100644
--- a/net/mac80211/mesh.h
+++ b/net/mac80211/mesh.h
@@ -216,6 +216,8 @@ struct rmc_entry {
 struct mesh_rmc {
 	struct hlist_head bucket[RMC_BUCKETS];
 	u32 idx_mask;
+	/* protects bucket[], fast-rx can run on several CPUs */
+	spinlock_t lock;
 };
 
 #define IEEE80211_MESH_HOUSEKEEPING_INTERVAL (60 * HZ)

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-10-25  3:05 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-25  3:05 [PATCH wireless] wifi: mac80211: protect the mesh RMC with a lock Joas Antonio dos Santos

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®