[PATCH] mac80211: mesh: Fix missing bounds checks in prepare_for_gate()

From: Amir Mohammad Jahangirzad

Date: Sat Aug 22 2026 - 12:52:29 EST


When moving or copying frames to a gate mpath, prepare_for_gate()
processes incoming packets by reading the MAC header and modifying
the packet to add Address Extension (AE) fields if necessary.

However, the function lacked proper bounds checking on the incoming
skb, exposing the code to potential crashes and out-of-bounds reads.
Specifically, the function calculates `hdrlen` from the frame control
field and then directly accesses the mesh header at `skb->data + hdrlen`
without checking if `skb->len` is actually large enough to hold this
data. If a truncated packet is processed, this can result in an
out-of-bounds read.

Furthermore, when the packet does not have the Address Extension flags,
the function calls `skb_push(skb, 2 * ETH_ALEN)`. It performs this push
without verifying if the `skb` actually has enough headroom available.
If a packet exhausts the available headroom, this will trigger a BUG()
in `skb_push` and cause a kernel crash.

This patch fixes these issues by validating `skb->len` before reading
any headers and checking `skb_headroom()` before modifying the packet.
`prepare_for_gate()` is modified to return an `int` (-EINVAL on
failure), and its caller is updated to gracefully drop malformed
packets instead of queueing them.

Signed-off-by: Amir Mohammad Jahangirzad <a.jahangirzad@xxxxxxxxx>
---
net/mac80211/mesh_pathtbl.c | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)

diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
index 03171cf00855..b344f305a85f 100644
--- a/net/mac80211/mesh_pathtbl.c
+++ b/net/mac80211/mesh_pathtbl.c
@@ -131,22 +131,32 @@ void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta)
spin_unlock_irqrestore(&mpath->frame_queue.lock, flags);
}

-static void prepare_for_gate(struct sk_buff *skb, char *dst_addr,
- struct mesh_path *gate_mpath)
+static int prepare_for_gate(struct sk_buff *skb, char *dst_addr,
+ struct mesh_path *gate_mpath)
{
struct ieee80211_hdr *hdr;
struct ieee80211s_hdr *mshdr;
int mesh_hdrlen, hdrlen;
char *next_hop;

+ if (skb->len < sizeof(struct ieee80211_hdr))
+ return -EINVAL;
+
hdr = (struct ieee80211_hdr *) skb->data;
hdrlen = ieee80211_hdrlen(hdr->frame_control);
+
+ if (skb->len < hdrlen + 6)
+ return -EINVAL;
+
mshdr = (struct ieee80211s_hdr *) (skb->data + hdrlen);

if (!(mshdr->flags & MESH_FLAGS_AE)) {
/* size of the fixed part of the mesh header */
mesh_hdrlen = 6;

+ if (skb_headroom(skb) < 2 * ETH_ALEN)
+ return -EINVAL;
+
/* make room for the two extended addresses */
skb_push(skb, 2 * ETH_ALEN);
memmove(skb->data, hdr, hdrlen + mesh_hdrlen);
@@ -169,6 +179,7 @@ static void prepare_for_gate(struct sk_buff *skb, char *dst_addr,
rcu_read_unlock();
memcpy(hdr->addr2, gate_mpath->sdata->vif.addr, ETH_ALEN);
memcpy(hdr->addr3, dst_addr, ETH_ALEN);
+ return 0;
}

/**
@@ -218,8 +229,10 @@ static void mesh_path_move_to_queue(struct mesh_path *gate_mpath,
if (WARN_ON(!skb))
break;

- prepare_for_gate(skb, gate_mpath->dst, gate_mpath);
- skb_queue_tail(&gate_mpath->frame_queue, skb);
+ if (prepare_for_gate(skb, gate_mpath->dst, gate_mpath) == 0)
+ skb_queue_tail(&gate_mpath->frame_queue, skb);
+ else
+ kfree_skb(skb);

if (copy)
continue;
--
2.55.0