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

sc_epq[MTW_BULK_RX] is a zero-length array: the 6 TX endpoint queues alias 14 KB of live mtw_softc fields; every ifconfig-up stamps over them and every channel change (sc_chan_group) forges the upper dword of epq[0].tx_data[3].ni, a live ieee80211_node pointer

Summary

if_mtwvar.h:323 declares sc_epq[MTW_BULK_RX] where MTW_BULK_RX==0 - a GNU zero-length array: sizeof(softc) excludes the queues while init's setup_tx_list memsets 6x2344=14064 bytes at offset 12032 over live tail fields (txpow/lna/rssi EEPROM tables, sc_mcu/rxq/txq). sc_chan_group @+12300 lands INSIDE epq[0].tx_data[3].ni (@+12296..12304): every channel change writes 0..3 into the top 32 bits of a live node pointer that tx-completion later dereferences and writes (refcount) - userspace-mappable address when the low dword is controllable. FreeBSD upstream declares sc_epq[MTW_EP_QUEUES] - DF-port regression. In-kernel structural proof via KLD probe on guest against real headers: sizeof(sc_epq)==0, exact alias mapped; corruption hardware-gated -> inconclusive with in-kernel proof. One-word dimension fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5258 Β· 5 files
FileTypeDescriptionSize
env.txt β€” 183 B view raw
fix.diff β€” 351 B view raw
mtwprobe.c β€” 7.0 KB view raw
probe_run.log β€” 1.8 KB view raw
verdict.json β€” 4.3 KB view raw

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

fix.diff authored and verified with 'git apply --check' against the pristine sys/ tree, but cannot be kernel-tested: the guest has no USB controller and no MT7601U device model, so neither baseline nor patched kernel behavior is exercisable. Hardware-gated: not_testable.

fix.diff (git-apply clean); VERDICT.md
↓ fix.diffper-fix-DF-5258

Confirmed kernel references

Detail

Exploit chain

plug MT7601U -> ifconfig wlan create/UP -> 14KB stamp over live tail fields -> channel change writes 0..3 into top dword of epq[0].tx_data[3].ni -> idle: ieee80211_free_node(0x200000000) at next detach/down teardown (wild write); in-flight: ieee80211_tx_complete on 0xNxxxxxxxx forged node -> panic, or with cmd_seq (<=0xFF) as byte 4 and an mmap'd user page at that address, refcount write / node_free on fully attacker-crafted node -> privesc plausible

Evidence (decisive lines)

probe_run.log (in-kernel, real if_mtwvar.h md5 0a8f339bbc49064bc903215a1c21063f): 'sizeof(sc->sc_epq) as declared = 0', 'sc_chan_group @+12300 INSIDE epq[0] tx_data[3] +52 (ni)' ; mtwprobe.c; VERDICT.md alias table

PoC changes

Probe module written from scratch; includes the verbatim mtw_setup_tx_list() and the real if_mtwvar.h from the guest /usr/src

Verified recommended fix

Size the array correctly: sc_epq[MTW_EP_QUEUES] (matches FreeBSD upstream)

Verdict

Structurally proven in-kernel: the DragonFly if_mtwvar.h:323 declares sc_epq[MTW_BULK_RX] where MTW_BULK_RX is the first enum constant (== 0) - a zero-length array - while FreeBSD upstream declares sc_epq[MTW_EP_QUEUES] (cgit.freebsd.org sys/dev/usb/wlan/if_mtwvar.h). sizeof(struct mtw_softc)=28928 therefore EXCLUDES the queues, yet mtw_init_locked() (if_mtw.c:4466-4467) calls mtw_setup_tx_list() on sc->sc_epq[0..5] on every interface UP, memset-stamping 6x2344=14064 bytes starting at softc offset 12032. A kld module built against the guest's real kernel headers printed the exact alias map (probe_run.log): the stamp region overlays live softc fields - txpow*/lna/rssi (TX-power EEPROM tables wiped -> device transmits at zeroed power), and critically sc_chan_group @+12300 lands INSIDE epq[0].tx_data[3].ni (@+12296..12304, +52 = upper 4 bytes of the node pointer): every channel change (mtw_set_chan, if_mtw.c:3758) rewrites the top 32 bits of a live ieee80211_node pointer with 0..3. An idle entry then carries forged ni=0x200000000 into mtw_unsetup_tx_list->ieee80211_free_node (detach) or an in-flight entry carries 0x0000000N_xxxxxxxx into ieee80211_tx_complete() (mtw_tx_free, if_mtw.c:2769) - a wild/forged node pointer, userspace-mappable in the 0xN00000000 range, dereferenced and written (refcount decrement). cmd_seq @+12304 and sc_detached @+12305 alias desc[] of the same entry. Live-kernel trigger needs an MT7601U dongle + ifconfig wlanX up (commonly auto-done by network managers), so the in-kernel impact is hardware-gated on this QEMU guest; the structure, sizes, offsets and stamping were all verified in-kernel with the real headers.