β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3290

mesh_parse_meshgate_action ignores the GANN IE's declared length: OOB read of up to 15 bytes re-broadcast to the mesh

Summary

The IE walk only guarantees frm[1]+2 bytes present, but the MESHGANN case copies the full 17-byte struct (15 payload bytes) without checking gann_len==IEEE80211_MESHGANN_BASE_SZ. A GANN IE declaring len=0 at the end of a frame padded past ieee80211_parse_action's total-length check makes the parser read 15 bytes beyond the IE; the parsed values are stored in the (leaking) gate table and re-broadcast in the propagated GANN, exfiltrating stale mbuf-cluster contents over the air. VERIFIED: captured victim re-broadcast carries every beyond-IE marker byte ('tx[29] ... b1 b3 b4 b5 b6 b7 b8 b9 b9 b9 b9 be bd'). Remote (established peer) bounded kernel-memory info leak over the air to all mesh nodes; leaked values also persist in the gate table. Distinct from DF-0286/0288. Fix validated (require full-size GANN IE): short GANN no longer propagated.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3290 Β· 5 files
FileTypeDescriptionSize
README.md β€” 2.3 KB ↓ raw
run.sh β€” 111 B view raw
run.log β€” 868 B view raw
verdict.json β€” 2.4 KB view raw
manifest.json β€” 475 B view raw

DF-3290 β€” mesh_parse_meshgate_action reads the 15-byte GANN body regardless of the IE's declared length; OOB bytes re-broadcast

File: sys/netproto/802_11/wlan/ieee80211_mesh.c (pass 2 audit)

What

mesh_parse_meshgate_action (:2574-2602) only guarantees that frm[1] + 2 bytes of each IE are present (loop VERIFY_LENGTH at :2583), then copies the full 17-byte GANN structure for any IEEE80211_ELEMID_MESHGANN element without comparing gann_len to IEEE80211_MESHGANN_BASE_SZ (15):

case IEEE80211_ELEMID_MESHGANN:
        gannie = (const struct ieee80211_meshgann_ie *) frm;
        /* copies gann_flags..gann_interval: 15 bytes past ie+len */
        ie->gann_seq = le32dec(&gannie->gann_seq); ...

A GANN IE declaring len=0 (or <15) placed at the end of a frame that is padded to satisfy ieee80211_parse_action's total length check (input.c requires only β‰₯17 bytes of action body) makes the parser read up to 15 bytes beyond the IE's declared extent β€” stale mbuf/cluster data. The parsed values (gate addr, seq, ttl, interval) are then re-broadcast in the propagated GANN (ieee80211_send_action at :2702), disclosing those bytes to every mesh node in radio range. Distinct from DF-0286 (lmetric total-length check) and DF-0288 (meshpeering early decode): this is the per-IE length check missing in the GANN walk.

Reproduced (stock kernel)

Injected (harness step 21): action body = 0d 02 (cat/act) + padding IE 00 06 PPΓ—6 + GANN IE header 7d 00 (declared len=0) + 15 trailing marker bytes (00 0d B2 B3..B8 B9Γ—4 BD BE) that the parser must NOT attribute to the IE.

Victim re-broadcast (captured at the radio):

tx[29] fc0=d0 len=43: ... 0d 02 7d 0f 00 0e b1 b3 b4 b5 b6 b7 b8 b9 b9 b9 b9 be bd

β†’ propagated GANN carries gate addr b3:b4:b5:b6:b7:b8, seq 0xb9b9b9b9, ttl 0xb1, interval 0xbebd β€” every one of the 15 bytes read from beyond the len=0 IE.

Build / run

sh findings/poc/DF-3286/build.sh
sh findings/poc/DF-3290/run.sh

Threat

Remote (established peer) OOB read of up to 15 bytes past the IE into stale mbuf-cluster contents, exfiltrated over the air to the whole mesh. kernleak bucket. Severity Low (bounded stale-data read; needs mesh vap). CWE-125. Fix: require frm[1] == IEEE80211_MESHGANN_BASE_SZ.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

short GANN (IE len=0) no longer propagated (baseline re-broadcast the markers); rejected by the new frm[1] != MESHGANN_BASE_SZ check

['fix.log: no len-43 propagated frame after step 21']
DragonFly dfbsd 6.5-DEVELOPMENT #1: Thu Sep 10 05:34:08 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (fix kernel)

Confirmed kernel references

Detail

Exploit chain

established peer sends CAT_MESH GANN action whose GANN IE declares len=0, padded so the total-length check passes and the trailing bytes double as a final valid IE -> parser reads 15 bytes past the IE -> gate stored from those bytes -> GANN re-broadcast to the whole mesh carries the out-of-bounds bytes

Evidence (decisive lines)

run.log: tx[29] propagated GANN containing the beyond-IE markers b3..be

PoC changes

harness step 21; the 15 trailing bytes double as a valid final IE (00 0d ...) so the walk survives to the propagation stage

Verified recommended fix

reject GANN IEs whose frm[1] != IEEE80211_MESHGANN_BASE_SZ (fix.diff hunk 6)

Verdict

mesh_parse_meshgate_action copies the 15-byte GANN body from any MESHGANN IE without checking gann_len, while the IE walk only guarantees frm[1]+2 bytes present. A GANN IE declaring len=0 at the end of a padded action frame made the victim parse 15 bytes beyond the IE and re-broadcast them: the propagated GANN (captured at the radio) carried the marker bytes as gate addr b3:b4:b5:b6:b7:b8, seq 0xb9b9b9b9, ttl 0xb1, interval 0xbebd. Remote OOB read (up to 15 bytes of stale mbuf data) exfiltrated over the air.