# DF-2357 VERDICT — reproduced (primitive confirmed); exploitation ceiling capped by allocator slack on stock x86_64

## Bottom line

The claimed bug is real and reproduces exactly as filed. The bound at
`sys/bus/u4b/wlan/if_rum.c:1057` — `m->m_pkthdr.len > (int)(MCLBYTES +
RT2573_TX_DESC_SIZE)` (2072) — is wrong by 16 bytes for the transfer
buffer declared at `if_rum.c:438` (`bufsize = MCLBYTES +
RT2573_TX_DESC_SIZE + 8` = 2080, with the 24-byte TX descriptor written
first at offset 0), leaving only 2056 bytes for the frame. The guard also
TRUNCATES (`if_rum.c:1060`) instead of dropping, so an oversized frame
(anything > 2072, e.g. a maximum-size 2346-byte 802.11 frame) becomes a
2072-byte frame that still overflows. Sibling `if_run.c:3091-3097` has
the correct shape and drops.

## How it was proven

Verbatim-interposition kernel harness (`rumtest/rumtest.c`): the guest
kernel source `if_rum.c` is sha256-gated
(`e11d7865440bbf95dce64336843a693e7251cd2747eb50ba0cfc6dbe8a992768`,
identical to the audited tree) and compiled, unmodified, into a kld
module. Only four externals are interposed by textual `#define`:
`usbd_transfer_start` / `usbd_transfer_submit` (synchronous fake USB
callback engine that runs the driver callback under `sc->sc_lock`,
exactly like the real `usb_callback_wrapper` lock protocol established
by `usbd_transfer_setup(..., &sc->sc_lock)` at `if_rum.c:488-489`) and
`ieee80211_tx_complete` / `ieee80211_free_node` (node lifetime). The copy
path is the REAL stock kernel `usbd_copy_in` / `usbd_m_copy_in`
(`sys/bus/u4b/usb_busdma.c:131-149, 203-209`) operating on a real
`struct usb_page_cache` whose buffer is the instrumented 2080-byte
allocation followed by a 0xC5 canary zone. The `usbd_get_page` Plain-PIO
case (`usb_busdma.c:119-126`) computes `res->buffer = pc->buffer +
offset` with no bounds check — byte-for-byte the same write set the
real DMA-buffer case produces at these offsets.

Entry path driven: the REAL `rum_raw_xmit` (`if_rum.c:3043`, the
`ic_raw_xmit` handler reachable from a monitor-mode vap / bpf raw
frame) with crafted mbuf chains at lengths 2048, 2056, 2057, 2060,
2064, 2072, 2073, 2346, plus direct `rum_bulk_write_callback` drives at
2072 and 3000.

Observed (run.baseline.log, deterministic across 3 runs):

- len 2048: copy 24..2071, submit_len 2072 <= 2080, canary clean (control)
- len 2056: copy 24..2079 exactly fills the buffer, submit_len 2080 == bufsize, canary clean (true boundary)
- len 2057: 1 canary byte overwritten at offset 2080 with 0x6f — exactly pattern byte 2056 of the frame (attacker-controlled content)
- len 2060/2064: 4/8 bytes
- len 2072: 16 bytes, offsets 2080..2095, bytes 6f 7c 89 96 a3 b0 bd ca d7 e4 f1 fe 0b 18 25 32 == frame pattern bytes 2056..2071
- len 2073 and 2346: m_pkthdr.len observed at submit time = 2072 (the truncation), still 16 bytes past the buffer, submit_len 2096 > 2080
- direct callback len 3000: clamped to 2072, same 16-byte overflow (the clamp lives in the callback, not the feed path)
- additionally the submitted USB transfer length itself exceeds the buffer size (2084..2096 vs 2080), so the host controller DMA program covers 16 bytes beyond the allocation

## Exploitability ceiling (honest assessment)

Reach requires an attached Ralink RT2501/RT2601/RT2573 USB dongle plus
raw-xmit privilege (monitor-mode vap or CAP_NET_RAW / bpf write) — an
already-privileged sender. That alone keeps this below High.

Even granting the privileged sender, the 16-byte overrun does NOT reach
an adjacent kernel object on the stock x86_64 kernel:

1. The TX buffer is allocated by `usb_pc_alloc_mem`
   (`usb_busdma.c:485-548`) via `bus_dmamem_alloc`.
2. On this guest (x86_64, hw.physmem 4257873920 < 4 GB, 32-bit DMA tag
   lowaddr 4 GB-1, alignment 1, `BUS_DMA_COHERENT` is not
   `BUS_DMA_NOCACHE`) the kmalloc path applies
   (`busdma_machdep.c:508-526`, `BUS_DMAMEM_KMALLOC` at :88).
3. `kmalloc(2080, M_DEVBUF, ...)` rounds up in `zoneindex()`
   (`kern_slaballoc.c:668-671`): the 2048..4095 range rounds to 256-byte
   granularity, so the chunk is 2304 bytes. Bytes 2080..2095 land inside
   the SAME chunk allocator slack. The page-boundary retry path
   (`check_kmalloc` -> `kmalloc(..., M_POWEROF2)` -> 4096) and the
   contigmalloc fallback (dedicated 4096-byte pages) are even roomier.

So on every stock allocation path the write stays inside the object own
padding: no adjacent-object overwrite, no length/pointer corruption, no
uid=0 route. No panic or instability occurred across repeated runs. The
only out-of-band effect on real hardware would be the over-long DMA
transfer (submit_len up to 2096) reading 16 bytes of uninitialized heap
slack and transmitting them over the air — a 16-byte heap-slack
exposure per oversized frame to a radio sniffer; not verified on
hardware (no dongle on the guest) and marginal.

The finding remains a legitimate memory-safety defect (CWE-787): it
violates the buffer contract the USB framework enforces nowhere else
(`usbd_copy_in` has no internal bounds check, `usb_busdma.c:131-149`),
and any future change in buffer sizing, tag alignment, or allocator
granularity (or a port with tighter packing) converts it into a real
cross-object heap overflow. Medium severity is the right call.

## Fix validation (A/B on the stock kernel)

`fix.diff` replaces the wrong bound + truncate with the correct bound
(frame must fit bufsize - descriptor = MCLBYTES + 8 = 2056) and DROPS
the frame (`rum_tx_free(data, 1); goto tr_setup;` — the if_run.c shape).

Module-level A/B is the complete validation here: the stock kernel
binary contains no rum code (`nm /boot/kernel/kernel` shows zero rum_
symbols; the driver ships only as `/boot/kernel/if_rum.ko`), so building
the patched driver as a module against the same running kernel exercises
the exact patched code with zero kernel-image involvement. A full kernel
rebuild would add nothing for this driver.

Results (run.fixed.log vs run.baseline.log):

- len 2048 and 2056: sent, submit_len unchanged, full-zone hex digest
  lines byte-identical to the stock driver (BIT-IDENTICAL, verified by
  string comparison of the RUMTESTHEX lines)
- len 2057, 2060, 2064, 2072, 2073, 2346 and both direct drives:
  DROPPED — no submit, canary fully intact, tx_complete with error
  status (oerrors accounting via the error completion path), driver
  continues to the next queued frame (goto tr_setup)

## Kernel references

- sys/bus/u4b/wlan/if_rum.c:438 (bufsize 2080)
- sys/bus/u4b/wlan/if_rum.c:1057-1065 (wrong bound + truncate + copy)
- sys/bus/u4b/wlan/if_rum.c:1080-1082 (xfer len roundup can hit 2096)
- sys/bus/u4b/usb_busdma.c:131-149 (usbd_copy_in, no bounds check)
- sys/bus/u4b/usb_busdma.c:203-209 (usbd_m_copy_in)
- sys/bus/u4b/usb_busdma.c:485-548 (buffer allocation path)
- sys/platform/pc64/x86_64/busdma_machdep.c:88,481-537 (kmalloc vs contigmalloc)
- sys/kern/kern_slaballoc.c:668-671 (zoneindex 2080 -> 2304 chunk)
- sys/bus/u4b/wlan/if_run.c:3091-3097 (correct sibling: bound + drop)

## 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.
