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

((struct rt2860_node *)ni)->wcid read from nodes allocated as plain ieee80211_node (missing ic_node_alloc override)

Summary

struct rt2860_node extends ieee80211_node with wcid, and rt2860_ampdu_rx_start/stop (live under IEEE80211_HT) read it - but the driver never overrides ic_node_alloc (attach hooks only node_free), so nodes are exactly sizeof(ieee80211_node): every wcid load reads 1 byte past the heap object and the garbage value (0-255) RMWs MMIO WCID_ENTRY(w)+4. OOB read of one heap byte per BA session start/stop; value never disclosed, impact limited to wrong WCID attribute word in hardware (robustness). HW_CRYPTO readers are compiled out. Fix: node allocator + maintain wcid in newassoc.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3751 Β· 5 files
FileTypeDescriptionSize
README.md β€” 1.8 KB ↓ raw
fix.diff β€” 1.5 KB view raw
verdict.json β€” 2.8 KB view raw
env.txt β€” 275 B view raw
manifest.json β€” 966 B view raw

DF-3751 β€” rt2860 reads rt2860_node.wcid from nodes allocated as plain ieee80211_node (OOB read)

struct rt2860_node (rt2860var.h:102-107) adds wcid (+ two dead rate-index arrays) after the embedded ieee80211_node, and four sites cast a net80211 node to it and read ->wcid:

  • rt2860_ampdu_rx_start rt2860.c:919 and rt2860_ampdu_rx_stop rt2860.c:934 β€” live (#ifdef IEEE80211_HT is satisfied in default builds), invoked by net80211 whenever an 802.11n Block-Ack session starts/stops for a node;
  • rt2860_set_key rt2860.c:3266 / rt2860_delete_key rt2860.c:3349 β€” dead (HW_CRYPTO is not defined anywhere in the tree).

But the driver never overrides ic->ic_node_alloc (attach hooks only ic_node_free, rt2860.c:352-353), so net80211 allocates nodes with the generic node_alloc = kmalloc(sizeof(struct ieee80211_node), M_80211_NODE, M_INTWAIT|M_ZERO) (wlan/ieee80211_node.c:917-923). Every ((struct rt2860_node *)ni)->wcid load reads one byte past the end of the heap allocation (allocator size-class padding or the next slab chunk). The garbage wcid (0-255) then indexes RT2860_WCID_ENTRY(wcid) + 4 = MMIO 0x1800 + 8*wcid + 4 (max 0x1ffc β€” inside the register aperture): a read-modify-write of the wrong WCID attribute word on every BA session event. No kernel-memory disclosure of the OOB byte; no kernel-RAM write. Severity: Low (CWE-125, wrong-HW-config / robustness).

Fix (fix.diff): give the driver its own rt2860_node_alloc (kmalloc(sizeof(struct rt2860_node)), matching rt2860_node_free's existing hook) and actually maintain rn->wcid in rt2860_newassoc (which was clearly the original intent β€” the field is otherwise written nowhere).

Status: source-certain; not runnable β€” audit guest has no Ralink NIC (env.txt).

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

Fix restores the author-intended node layout (struct rt2860_node exists precisely for wcid); compile-consistent; runtime validation needs hardware.

fix.diff (git apply --check passes against pristine sys/)
↓ fix.diffper-fix-DF-3751

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (alloc-site proof), fix.diff (adds rt2860_node_alloc + sets rn->wcid in newassoc; git apply --check passes)

PoC changes

Analysis-only; no runtime PoC possible without the NIC.

Verified recommended fix

Install ic_node_alloc = rt2860_node_alloc (kmalloc sizeof(struct rt2860_node)) and set ((struct rt2860_node *)ni)->wcid = wcid in rt2860_newassoc.

Verdict

The driver declares struct rt2860_node { struct ieee80211_node ni; uint8_t wcid; ... } (rt2860var.h:102-107) and reads ((struct rt2860_node )ni)->wcid in the live AMPDU callbacks rt2860_ampdu_rx_start (rt2860.c:919) and rt2860_ampdu_rx_stop (rt2860.c:934) -- and in the compiled-out HW_CRYPTO key paths (rt2860.c:3266/3349; HW_CRYPTO is never defined anywhere in the tree) -- but never installs an ic_node_alloc override (no 'node_alloc' in the file; attach hooks only ic_node_free at rt2860.c:353). net80211's generic node_alloc allocates exactly sizeof(struct ieee80211_node) (wlan/ieee80211_node.c:917-923), so the wcid load reads one byte past the end of the heap object; the value (allocator padding / adjacent slab bytes) then indexes MMIO RT2860_WCID_ENTRY(wcid)+4 (0x1800+8wcid+4 <= 0x1ffc -- inside the register aperture, so no kernel-RAM write). Impact: an out-of-bounds read feeding a wrong-hardware-register RMW for every BA session start/stop; the byte read is not disclosed to the attacker. Certain at source level, low impact. Not reproduced: guest has no Ralink NIC (env.txt).