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

Heap buffer overflow in USB RX frame aggregation: m_getcl (2048B) too small for m_copydata of up to 4080B

Summary

When Ralink USB device batches multiple 802.11 frames into one USB bulk transfer (DMA aggregation) driver copies each non-final aggregated frame into freshly allocated mbuf. mbuf allocated with m_getcl() providing MCLBYTES (2048-byte) cluster but m_copydata() writes dmalen+sizeof(struct rt2870_rxd) bytes into it. dmalen up to ~4076 (bounded only by RUN_MAX_RXSZ=4096) so copy overflows 2048-byte cluster by up to 2032 bytes. m_copydata does not bounds-check destination (kern/uipc_mbuf.c:1671 KASSERT only validates source). Source sc->rx_m correctly allocated m_getjcl MJUMPAGESIZE (4096). Two attack vectors: (1) Wireless adjacent-network rogue AP sends burst of large data frames firmware batches them overflow triggers (2) USB physical malicious device impersonating Ralink sends crafted bulk IN. Impact: kernel heap corruption arbitrary kernel code execution or panic DoS.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2358 Β· 6 files
FileTypeDescriptionSize
VERDICT.md verdict gate analysis + RX-aggregation dest-cluster overflow trace 3.7 KB ↓ raw
fix.diff suggested-fix allocate dest mbuf with m_getjcl(MJUMPAGESIZE) like the source rx_m 699 B view raw
build.sh build-script documents HW gate 159 B view raw
run.sh run-script prints gate proof 290 B view raw
env.txt environment guest env 1.1 KB view raw
wifi_gate.txt gate-proof usbconfig empty, no wlan iface 311 B view raw
VERDICT.md verdict gate analysis + RX-aggregation dest-cluster overflow trace
↓ download raw

DF-2358 β€” Heap buffer overflow in USB RX frame aggregation (sys/bus/u4b/wlan/if_run.c)

Verdict: NOT REPRODUCED (HW-gated); REAL BUG IN SOURCE (defense-in-depth fix warranted)

Hardware gate (why the PoC cannot run on this guest)

run is the Ralink RT2700U/RT2800U/RT3000U/RT3900E 802.11abgn USB wifi driver. It attaches only when a matching Ralink USB wifi dongle is plugged in. The audit QEMU/KVM guest has no USB device and no wifi interface:

$ usbconfig list               # No device match or lack of permissions.
$ ifconfig -l                  # vtnet0 lo0   (no wlan/run)
$ pciconf -l | grep -iE "ralink|148f|1435|0789"   # (no Ralink USB wifi chip)
$ kldstat                      # kernel + ehci.ko + xhci.ko only

The RX aggregation path run_bulk_rx_callback runs only when the run driver has attached to a Ralink USB wifi device and is receiving batched frames. With no such device the path never executes; the unprivileged maxx user cannot plug a USB dongle into the QEMU guest, and there is no adjacent rogue-AP threat (no radio).

Source trace β€” the bug is REAL (sys/bus/u4b/wlan/if_run.c)

run_bulk_rx_callback (if_run.c:2897-...) splits an aggregated USB bulk transfer into individual 802.11 frames. Bounds (if_runvar.h:26-27):

#define RUN_MAX_RXSZ  MIN(4096, MJUMPAGESIZE)   /* 4096 */

The source buffer sc->rx_m is correctly allocated with m_getcl/m_getjcl (MJUMPAGESIZE = 4096), so the source of the copy is in bounds. The problem is the destination:

m->m_pkthdr.len = m->m_len = xferlen;                       /* if_run.c:2985 */
for(;;) {
    dmalen = le32toh(*mtod(m, uint32_t *)) & 0xffff;        /* device-controlled 16-bit */
    if ((dmalen >= (uint32_t)-8) || (dmalen == 0) || ((dmalen & 3) != 0)) break;
    if ((dmalen + 8) > (uint32_t)xferlen) break;            /* if_run.c:2996: bound by xferlen, not dest */
    if ((xferlen -= dmalen + 8) <= 8) { ... run_rx_frame(sc, m, dmalen); break; }  /* final frame uses 4096-byte src buf */
    /* aggregated (non-final) frames: copy into a NEW m_getcl cluster */
    m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);              /* if_run.c:3013: cluster = MCLBYTES = 2048 */
    ...
    m_copydata(m, 4, dmalen + sizeof(struct rt2870_rxd),    /* if_run.c:3023 */
               mtod(m0, void *));                           /* writes dmalen+rxd_size into 2048-byte cluster */

dmalen is bounded only by xferlen (≀ RUN_MAX_RXSZ = 4096), so for a non-final aggregated frame dmalen + sizeof(struct rt2870_rxd) can reach ~4080 bytes β€” but m0 is a m_getcl cluster of only MCLBYTES = 2048 bytes. m_copydata does not bounds-check the destination (the KASSERT at kern/uipc_mbuf.c:1671 validates only the source), so the copy overflows the 2048-byte cluster by up to ~2032 bytes into adjacent slab memory.

Two attack vectors: (1) wireless adjacent-network rogue AP bursts large data frames the firmware aggregates; (2) a malicious USB device impersonating Ralink sends a crafted bulk IN. Impact: kernel heap corruption β†’ arbitrary kernel code execution or panic.

Exploit chain status

Not pursuable β€” primitive (heap overflow of destination cluster) requires a Ralink USB wifi dongle or adjacent radio (absent) β€” valid Phase-6 hard blocker: dead path at runtime on this guest. On hardware this is a write-capable heap overflow.

PoC changes

None. No Ralink USB wifi dongle on guest; verified by source trace only.

Use a destination mbuf large enough for dmalen + sizeof(rt2870_rxd) (e.g. m_getjcl(MJUMPAGESIZE)) or bound dmalen against MCLBYTES - sizeof(rt2870_rxd). See fix.diff (matches finding proposal: allocate the destination with a cluster that can hold the maximum possible copy).

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

not_testable: PoC cannot run on this guest (HW-gated, no target device). fix.diff validated by git apply --check (clean) + line-accurate source trace confirming it closes the cited path.

git apply --check findings/poc/DF-2358/fix.diff -> OK (clean apply). No runtime test possible (HW-gated).
↓ fix.diffn/a (no target HW/device on this guest)

Confirmed kernel references

Detail

Exploit chain

none β€” valid hard blocker (driver/device path dead at runtime on this guest: no target HW / no attached device). No unprivileged->root path.

Evidence (decisive lines)

usbconfig list -> No device match or lack of permissions.; pciconf -l -> no target controller HW; ifconfig -l -> vtnet0 lo0; kldstat -> kernel/ehci/xhci only; ls /dev/<target> -> No such file or directory; id maxx -> uid=1001 groups=1001 (not operator). Source confirmed at cited lines.

PoC changes

Created findings/poc/DF-2358/{VERDICT.md,fix.diff,build.sh,run.sh,env.txt,gate_proof.txt,manifest.json}. No PoC source (HW-gated).

Verified recommended fix

change m0=m_getcl to m_getjcl(MJUMPAGESIZE). Full git-apply-able diff in findings/poc/DF-2358/fix.diff (git apply --check OK).

Verdict

NOT REPRODUCED (HW-gated). The bug is REAL in source (traced line-by-line). run heap overflow in USB RX frame aggregation (non-final frame m_getcl=2048, copy up to ~4080); no Ralink USB wifi. Gate confirmed via usbconfig list (No device match / no /dev/ugen*), pciconf -l (no target controller HW), ifconfig (vtnet0 lo0 only), kldstat (no target module), and ls /dev (no target nodes). maxx (uid 1001, not in operator) cannot reach any /dev/usbctl write path.