[PATCH wireless] wifi: mac80211: reload eth header after mesh fast forward fallback
From: Joas Antonio dos Santos
Date: Sat Oct 24 2026 - 21:47:25 EST
ieee80211_rx_mesh_data() sets eth = skb->data and, for unicast frames
that are not for us, first tries ieee80211_rx_mesh_fast_forward(). If
that returns false, the slow path builds the forwarding header from
eth->h_dest and eth->h_source.
ieee80211_rx_mesh_fast_forward() calls skb_linearize() before it looks
at the next hop's TX BA session, and returns false when tid_tx exists
but is not HT_AGG_STATE_OPERATIONAL yet (an ADDBA to the next hop is
still pending). For a non-linear skb, skb_linearize() goes through
__pskb_pull_tail(), which calls pskb_expand_head() when the paged data
does not fit in the tailroom. That allocates a new head and frees the
old one, so eth then points into freed memory.
The skb can be non-linear here, for example, for A-MSDU subframes:
ieee80211_amsdu_to_8023s() sets reuse_frag when the driver passes up
head_frag skbs, and __ieee80211_amsdu_copy() then copies only the first
32 + mesh header bytes into a small new head and attaches the rest of
the subframe as page fragments.
The stale eth is then read by ieee80211_fill_mesh_addresses(), which
copies 12 bytes from the freed head into addr3/addr4 of the forwarded
frame. hdr.addr3 is also passed to mesh_path_error_tx() when no next
hop is found.
Reload eth from skb->data when the fast path declines the frame. The
slow path already deals with non-linear skbs on its own (skb_cow_head()
and skb_linearize() on fwd_skb, after the addresses have been copied).
Fixes: 8b0f5cb6bc7c ("wifi: mac80211: use mesh header cache to speed up mesh forwarding")
Signed-off-by: Joas Antonio dos Santos <joasantonio108@xxxxxxxxx>
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 reproduced it at runtime:
mac80211_hwsim hands up linear skbs without head_frag, so reuse_frag
is never set there and skb_linearize() is a no-op on this path.
Hitting it needs a driver that passes up head_frag or paged skbs and a
next hop whose TX BA session for the TID is still being set up.
net/mac80211/rx.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index ecee9348c7..fb49f3974f 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -3047,6 +3047,9 @@ ieee80211_rx_mesh_data(struct ieee80211_sub_if_data *sdata, struct sta_info *sta
ieee80211_rx_mesh_fast_forward(sdata, skb, mesh_hdrlen))
return RX_QUEUED;
+ /* the fast path may have linearized the skb and reallocated its head */
+ eth = (struct ethhdr *)skb->data;
+
ieee80211_fill_mesh_addresses(&hdr, &hdr.frame_control,
eth->h_dest, eth->h_source);
hdrlen = ieee80211_hdrlen(hdr.frame_control);