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

rxeof trusts the device-fed 12-bit RX frame length against a 2048-byte cluster: m_len up to 4095 β†’ up to 2047-byte OOB read past the mbuf cluster into ieee80211_input (kernel heap disclosure / parser crash)

Summary

rt2661_rx_intr finalizes the RX mbuf with the 12-bit descriptor length straight from the DMA'd descriptor - up to 4095 - while the mbuf is a 2048-byte cluster. No clamp exists anywhere on the path; ieee80211_input then parses up to 2047 bytes past the cluster as frame content, deliverable to the stack/userland (monitor-mode capture) or crashing net80211 parsers. rt2860 sibling knowns do not cover rxeof length trust. DF-3604 family wifi instance. Clamp-to-MCLBYTES fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3966 Β· 11 files
FileTypeDescriptionSize
README.md β€” 1.2 KB ↓ raw
VERDICT.md β€” 1.8 KB ↓ raw
trigger.c β€” 1.9 KB view raw
build.sh β€” 67 B view raw
run.sh β€” 91 B view raw
run.log β€” 12 B view raw
env.txt β€” 1021 B view raw
fix.diff β€” 604 B view raw
build.log β€” 37.9 KB view raw
manifest.json β€” 370 B view raw
verdict.json β€” 3.4 KB view raw

DF-3966 β€” rt2661_rx_intr trusts the device-fed 12-bit frame length against a 2 KB cluster: up to 2047-byte OOB read into ieee80211_input

File: sys/dev/netif/ral/rt2661.c (pass 2; no known findings on this file; rt2860 sibling knowns DF-3748..3753 do not cover rxeof length trust)

What

rt2661.c:1101–1102 sets m->m_pkthdr.len = m->m_len = (flags>>16)&0xfff straight from the DMA'd RX descriptor β€” up to 4095 β€” while the mbuf is a 2048-byte cluster (m_getcl at 1055; MCLBYTES=2048, sys/sys/param.h:497). There is no clamp. The over-long mbuf is passed to ieee80211_input (1132–1138), so up to 2047 bytes beyond the cluster are parsed as frame content and can be delivered to the stack/userland (monitor-mode capture, UDP payload) β€” a device-gated kernel heap info leak; malformed OOB header fields can equally panic the parsers.

Build / Run (hardware recipe)

Requires a Ralink RT2561/RT2661 NIC (guest has none β€” env.txt; if_ral.ko ships in /boot/kernel). See trigger.c steps: monitor mode + one crafted descriptor (flags[27:16] > 2048) + tcpdump capture.

Expected

Captured frames contain bytes from beyond the mbuf cluster (kernel heap), or a parser panic on garbage OOB bytes.

VERDICT.md
↓ download raw

DF-3966 verdict β€” source-certain OOB read, hardware-gated

Reproduced?

No: the audit guest has no Ralink device (pciconf: no vendor 0x1814; env.txt). if_ral.ko is present in /boot/kernel (1.59 MB) and one kldload away on any host with the NIC.

Why the bug is real

  • rt2661.c:1017-1021: desc = &sc->rxq.desc[sc->rxq.cur]; flags read from the DMA'd descriptor (device-written).
  • rt2661.c:1101-1102: m->m_pkthdr.len = m->m_len = (le32toh(desc->flags) >> 16) & 0xfff β€” the ONLY bound is the 12-bit field mask (4095).
  • rt2661.c:1055/706: the RX mbufs are m_getcl(...) clusters β€” MCLBYTES = 1<<11 = 2048 (sys/sys/param.h:494-497, x86_64).
  • rt2661.c:1129-1138: mtod(m, ...) + ieee80211_input(ni, m, ...) / ieee80211_input_all β€” net80211 walks the frame using m_len: everything in (2048, 4095] is read past the cluster.
  • No intermediate clamp exists anywhere on the path (checked: rt2661_rx_intr is the only consumer of the descriptor length).

Threat model / severity

Device-fed: requires malicious/compromised NIC firmware or a DMA platform attack (same class as DF-3748..3753 for the rt2860 sibling β€” those covered AID truncation, wcid UAF, TXWI pool, wcid byte, retrycnt, rate lookups; none of them is this rxeof length trust). A merely buggy NIC that sets the 12-bit length above 2048 also triggers it. Medium: kernel heap disclosure up to 2047 B/frame, repeatable per descriptor.

Fix

fix.diff clamps the finalized length to MCLBYTES before ieee80211_input.

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

fix.diff clamps the finalized m_len/m_pkthdr.len to MCLBYTES; git apply --check clean. Runtime validation impossible without the NIC fix.diff applied to the guest /usr/src copy and the if_ral.ko module rebuilt clean with -Werror (build.log; combined with the other two rt2661 fix.diffs of this batch); runtime validation still impossible without the NIC.

fix.diff; fix.diff applied to the guest /usr/src copy and the if_ral.ko module rebuilt clean with -Werror (build.log; combined with the other two rt2661 fix.diffs of this batch); runtime validation still impossible without the NIC.
↓ fix.diffper-fix-DF-3966

Confirmed kernel references

Detail

Exploit chain

device writes an RX descriptor with flags[27:16] in (2048,4095] and a short frame in the cluster -> m_len=m_pkthdr.len=N>2048 -> ieee80211_input parses headers/payload OOB past the 2KB cluster -> leaked kernel heap bytes travel as frame payload to userland (monitor mode capture / UDP) or crash net80211 parsers.

Evidence (decisive lines)

VERDICT.md (length/cluster math + path:line), trigger.c (hardware recipe), env.txt (no vendor 0x1814 on guest; if_ral.ko present), fix.diff (MCLBYTES clamp, git apply --check clean)

PoC changes

No runnable PoC: needs RT2561/RT2661 hardware or a PCIe device emulator posting a crafted descriptor; trigger.c documents the on-hardware steps.

Verified recommended fix

Clamp the device-fed length to MCLBYTES before handing the mbuf to ieee80211_input (fix.diff).

Verdict

Source-certain device-fed OOB read: rt2661_rx_intr finalizes the RX mbuf with m->m_pkthdr.len = m->m_len = (le32toh(desc->flags)>>16)&0xfff (rt2661.c:1101-1102) β€” up to 4095 from the DMA'd descriptor β€” while the mbuf is a 2048-byte m_getcl cluster (rt2661.c:1055; MCLBYTES=1<<11, sys/sys/param.h:497); no clamp exists anywhere on the path, so ieee80211_input/ieee80211_input_all (rt2661.c:1132-1138) parse up to 2047 bytes past the cluster and can deliver them to the stack/userland (monitor capture, UDP payload) or panic parsers on garbage OOB fields. Device-gated (malicious/compromised NIC or DMA attack; a buggy NIC setting the 12-bit field above 2048 also qualifies). Not reproduced: no Ralink NIC on the audit guest (env.txt); if_ral.ko ships in /boot/kernel. rt2860 sibling knowns DF-3748..3753 do not cover rxeof length trust.