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

Heap OOB read in bwn_dma_rxeof via dr_rx_bufsize / descriptor bytecnt mismatch

Summary

bwn_dma_rxeof at if_bwn.c:5486 checks frame_len against dr->dr_rx_bufsize (=BWN_DMA0_RX_BUFFERSIZE=IEEE80211_MAX_LEN~2316) but actual RX DMA buffer is MCLBYTES=2048. Descriptor bytecnt at :5733 set to MCLBYTES-sizeof(bwn_rxhdr4)=2028. For frame_len in (2018,2316] the check passes, m_len set to len+dr_frameoffset at :5512 exceeding 2048-byte cluster, m_adj leaves m_len up to 2316 with only 2018 bytes valid backing store. ieee80211_input walks up to ~298 bytes past cluster -> heap info leak or panic. Unauthenticated wireless attacker sends large Beacon/Probe Response with many IEs. Default config. Fix: set dr_rx_bufsize = MCLBYTES - sizeof(bwn_rxhdr4) or check len against dr_rx_bufsize - dr_frameoffset.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1131 Β· 12 files
FileTypeDescriptionSize
harness.c trigger-source userspace replica of bwn_dma_rxeof length check + WITH-FIX pass 5.6 KB view raw
build.sh build-script cc -O2 -Wall -Wextra -o harness harness.c 114 B view raw
run.sh run-script ./harness 60 B view raw
build.log build-log final build, full output 8 B view raw
run.log run-log decisive run incl CONFIRMED + FIX VALIDATED 1.8 KB view raw
env.txt environment uname, cc, pciconf (no WiFi HW) 241 B view raw
fix.diff suggested-fix add len+frameoffset>MCLBYTES guard to bwn rxeof drop check 791 B view raw
fix_validation.txt fix-validation apply-check + compile (rc=0) + harness fix-demo 1.7 KB view raw
VERDICT.md verdict full narrative 2.4 KB ↓ raw
README.md readme summary + repro 2.5 KB ↓ raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme summary + repro
↓ download raw

DF-1131 β€” bwn DMA rxeof dr_rx_bufsize / cluster-size mismatch OOB read

Verdict

CONFIRMED (source-trace + harness) β€” INCONCLUSIVE on-guest (HW-gated). The bug is real; it cannot be triggered on the audit QEMU guest because the guest has no Broadcom BCM43xx (bwn) WiFi adapter (virtio net only).

Bug (one line)

bwn_dma_rxeof() checks frame_len against dr->dr_rx_bufsize (= BWN_DMA0_RX_BUFFERSIZE = IEEE80211_MAX_LEN = 2312) but the RX mbuf cluster is only MCLBYTES (2048), so a device-reported frame_len in (2018, 2312] passes the check yet makes m_len = len + dr_frameoffset exceed the 2048-byte cluster.

Mechanism (path:line)

  • if_bwn.c:2778 β€” dr->dr_rx_bufsize = BWN_DMA0_RX_BUFFERSIZE; (BWN_DMA0_RX_BUFFERSIZE = IEEE80211_MAX_LEN; if_bwnreg.h:464).
  • IEEE80211_MAX_LEN = 2300 + 4 + (3+1+4) = 2312 (ieee80211.h:1297); MCLBYTES = 2048.
  • if_bwn.c:5674 β€” m = m_getcl(...) allocates an MCLBYTES (2048) cluster.
  • if_bwn.c:5688 β€” m->m_len = m->m_pkthdr.len = MCLBYTES;
  • if_bwn.c:5470 β€” len = le16toh(rxhdr->frame_len); (device-reported).
  • if_bwn.c:5486 β€” if (len > dr->dr_rx_bufsize) checks against 2312, not 2048.
  • if_bwn.c:5512 β€” m->m_len = m->m_pkthdr.len = len + dr->dr_frameoffset; (frameoffset=30) exceeds 2048 for len > 2018, but the cluster is only 2048 -> net80211 reads past the cluster when processing the frame.

Trigger / threat model

A malicious/rogue 802.11 frame (or a firmware bug) that makes the device report frame_len in (2018, 2312] is kept (it would be dropped only above 2312) and processed with an m_len larger than the cluster. net80211 then over-reads up to (2312+30-2048)=294 bytes of adjacent kernel heap; those bytes appear in the delivered frame payload (info leak) or hit an unmapped page (panic). Requires a Broadcom bwn adapter; attacker is within radio range.

Reproduction on the audit guest

Not possible β€” no bwn WiFi hardware. harness.c is a userspace replica of the length-check arithmetic, built and run as unprivileged maxx, proving the OOB and that the fix (drop when len + frameoffset > MCLBYTES) yields 0 OOB.

Build / run

./build.sh && ./run.sh

Expected: DF-1131: CONFIRMED heap OOB read primitive (up to 294 bytes past cluster) then DF-1131 FIX: VALIDATED - cluster-bound guard drops all overrun frames.

Fix

fix.diff adds (u_int)len + dr->dr_frameoffset > MCLBYTES to the existing drop check. Applies cleanly; compiles in the GENERIC kernel build (rc=0).

VERDICT.md verdict full narrative
↓ download raw

DF-1131 β€” VERDICT

Verdict: CONFIRMED via source-trace + userspace harness. On-guest: INCONCLUSIVE (HW-gated β€” no Broadcom bwn WiFi adapter on the QEMU guest).

Root-cause confirmation

bwn_dma_rxeof() validates the device-reported frame_len against the wrong constant. dr->dr_rx_bufsize is set to BWN_DMA0_RX_BUFFERSIZE = IEEE80211_MAX_LEN = 2312 (if_bwn.c:2778, if_bwnreg.h:464, ieee80211.h:1297), but the actual RX buffer is an mbuf cluster of MCLBYTES = 2048 allocated by m_getcl() (if_bwn.c:5674) with m_len = MCLBYTES (if_bwn.c:5688). The check at if_bwn.c:5486 (if (len > dr->dr_rx_bufsize)) therefore drops only len > 2312, while any len in (2018, 2312] is kept and immediately produces m_len = len + dr_frameoffset (if_bwn.c:5512, frameoffset=30) greater than the 2048-byte cluster. net80211 processes the mbuf reading up to m_len bytes from a 2048-byte backing cluster -> heap OOB read of up to (2312+30-2048)=294 bytes.

Evidence

  • harness.c (run as unprivileged maxx) shows 4/7 frame_len values (2019, 2048, 2100, 2312) pass the kernel check but overrun the 2048-byte cluster by 1..294 bytes; a concrete model with frame_len=2100 reads 82 OOB bytes of an adjacent heap marker.
  • Full run: run.log. Build: build.log. Environment (no WiFi HW): env.txt.

Exploit chain / impact

This is an OOB read (CWE-125), not a write primitive, so no uid=0 chain. Realistic impact ceiling: kernel heap info leak up to ~294 bytes per malicious frame (the over-read bytes flow into the received 802.11 frame payload), or a panic if the overrun reaches an unmapped page. AV:A, requires bwn hardware + radio-range attacker.

Fix validation

fix.diff extends the drop check to (u_int)len + dr->dr_frameoffset > MCLBYTES. - git apply --check -p1 => OK. - Compiles in the GENERIC kernel build (nativekernel rc=0; if_bwn.o built clean). - Harness "WITH FIX" pass: 0/7 OOB cases remain. - fix_status: not_testable (HW-gated runtime; apply-check + compile + harness fix-demo + trace all pass).

PoC changes

Evidence pack authored from scratch (seeded dir was empty): harness.c (+ WITH-FIX pass), build.sh, run.sh, fix.diff, VERDICT.md, manifest.json, logs.

Kernel refs (confirmed during verification)

sys/dev/netif/bwn/bwn/if_bwn.c:2778, :5470, :5486, :5512, :5674, :5688; sys/dev/netif/bwn/bwn/if_bwnreg.h:464; sys/netproto/802_11/ieee80211.h:1297.

Fix verification

not_testable

compile+harness validated

module build rc=0 + harness 0 OOB

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed+harness. bwn_dma_rxeof frame_len 2048-2312 passes check but exceeds MCLBYTES cluster -> 294B heap OOB read. No bwn WiFi.