Unvalidated firmware-returned SRAM offsets used as kernel write pointers (lanai TX/RX rings, intr_coal_delay_ptr, irq_claim/irq_deassert, RSS table and key): OOB MMIO writes past the BAR mapping on a hostile device; int16_t ring-mask truncation on device ring sizes
Summary
Every GET_*_OFFSET response (cmd.data0, device-written) becomes a raw pointer into the mapped SRAM and is then written through, unchecked: intr_coal_delay_ptr (live via root sysctl), irq_deassert/irq_claim, TX/RX lanai descriptor rings PIO-written by submit paths, 128-byte RSS table loop and 16-byte RSS key. Offsets >= sram_size produce kernel MMIO writes past the pmap_mapdev window with partially host-chosen data. Related: rx ring mask/cl_size are int16_t fed by device ring sizes - entries >= 32768 wrap the mask negative. The file already validates the adopt-path offset; these loci lack the same check. Hostile/compromised PCIe device (worst sink: RSS table - 128 host-derived bytes at a device-chosen OOB offset). Helper+checks fix git-apply-clean.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3720 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.7 KB | β raw | |
| VERDICT.md | β | 1.4 KB | β raw | |
| fix.diff | β | 3.3 KB | view raw | |
| verdict.json | β | 2.5 KB | view raw | |
| manifest.json | β | 863 B | view raw | |
| env.txt | β | 583 B | view raw |
DF-3720 β Firmware-returned SRAM offsets used as kernel write pointers with no validation (hostile-device family); plus int16_t ring-mask truncation
Vulnerability
sys/dev/netif/mxge/if_mxge.c repeatedly turns a device-returned offset
(mxge_send_cmd response cmd.data0) into a raw pointer into the mapped
SRAM BAR (sc->sram, size sc->sram_size :4290) and then writes through
it, with no range check anywhere:
| Locus | Command | Sink |
|---|---|---|
mxge_reset :1207-1215 |
GET_INTR_COAL_DELAY_OFFSET / GET_IRQ_ACK_OFFSET / GET_IRQ_DEASSERT_OFFSET |
*sc->intr_coal_delay_ptr = htobe32(...) write :1222 and live sysctl write mxge_change_intr_coal :1339 (*sc->intr_coal_delay_ptr = ...); irq_claim stores :2717-2718/:2779-2780/:2853-2854 |
mxge_slice_open :3255-3283 |
GET_SEND_OFFSET / GET_SMALL_RX_OFFSET / GET_BIG_RX_OFFSET |
tx->lanai / rx_*->lanai β descriptor rings written by mxge_submit_req/mxge_submit_8rx PIO (e.g. :1580, :2030-2036) |
mxge_open :3351-3364 |
GET_RSS_TABLE_OFFSET |
128-byte table write loop :3363-3364 |
mxge_open :3373-3384 |
GET_RSS_KEY_OFFSET |
16-byte key write loop :3382-3383 |
A hostile/compromised NIC (or one returning garbage after a fault) supplies
an offset β₯ sram_size and the kernel performs MMIO writes past the
pmap_mapdev window β kernel-VA memory corruption with partially
attacker-chosen data (RSS table bytes are host-derived; intr_coal value is
a root-settable int; descriptors contain DMA addresses).
Related same-family sloppiness in the same flow: mxge_rx_ring_t.mask
and cl_size are int16_t (if_mxge_var.h:65-66) while the entry counts
derive from the NIC-returned GET_RX_RING_SIZE/GET_SEND_RING_SIZE
(mxge_alloc_slices :3960-3961, mxge_alloc_rings :3194-3196): a
ring size β₯ 512 KiB (entries β₯ 32768) wraps the mask negative
(:3007-3009) and idx = rx->cnt & rx->mask sign-extends garbage.
Contrast: mxge_adopt_running_firmware does bounds-check its
device-supplied header offset against sc->sram_size (:867) β the check
pattern exists in the file; the loci above simply never got it.
Threat model
Hostile/faulting PCIe device (same premise as DF-3716/3718 and the igb/bge/em device-trust findings). Not reachable by network input alone.
Verification
Hardware-gated; source-certain; inconclusive per family convention.
Fix
fix.diff adds mxge_sram_range_valid() and applies it at all four loci
(intr-coal/ack/deassert offsets, both send-offset branches, both rx-offset
commands, RSS table and key), failing open with ENXIO/EIO. git-apply
clean. (int16_t mask widening is a larger mechanical change; flagged in
the diff comment in the finding text only.)
DF-3720 VERDICT
Status: inconclusive (hardware-gated), confidence: certain source-level.
Source proof
- Every locus in the README table: cmd.data0 (device response) -> pointer into sc->sram -> unconditional write. No sram_size comparison anywhere in mxge_reset (:1207-1215), mxge_slice_open (:3255-3283), or the RSS table/key writes (:3362-3364, :3379-3384).
- The check pattern demonstrably exists in-file: mxge_adopt_running_firmware validates its device offset against sc->sram_size at :867.
- int16_t mask/cl_size (if_mxge_var.h:65-66) fed by device ring sizes (:3960, :3194-3196) -> entries >= 32768 wraps negative (:3007-3009).
- Sinks are reachable on every interface up/reset (attach does reset+open), and intr_coal_delay_ptr is writable live by root via sysctl (:1339).
Not reproduced
No Myricom device on the guest (env.txt); offsets come from the device.
Fix
fix.diff: mxge_sram_range_valid() applied at all four loci (git apply --check clean). fix_status=not_testable (no hardware to build against).
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_testablefix.diff git apply --check clean; not buildable without mxge hardware.
findings/poc/DF-3720/fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
findings/poc/DF-3720/VERDICT.md; fix.diff (mxge_sram_range_valid() at all four loci, git-apply clean); env.txt
PoC changes
No seed PoC; hardware-gated.
Verified recommended fix
Add mxge_sram_range_valid(sc, off, len) and validate every firmware-returned offset before first use; widen mask/cl_size to int.
Verdict
Source-certain, hardware-gated (hostile-device premise, DF-3716/3718 family): if_mxge.c converts firmware-returned offsets (mxge_send_cmd cmd.data0) into raw pointers into the mapped SRAM BAR and writes through them with no range check - intr_coal_delay_ptr/irq_deassert/irq_claim (mxge_reset :1207-1215, written :1222 and via sysctl :1339, claimed :2717-2718), the TX/RX lanai descriptor rings (mxge_slice_open :3255-3283, PIO-written by mxge_submit_req/mxge_submit_8rx), the 128-byte RSS indirection table (:3362-3364) and the 16-byte RSS key (:3379-3384). A device returning an offset >= sram_size gets kernel MMIO writes past the pmap_mapdev window. Related: mxge_rx_ring_t.mask/cl_size are int16_t (if_mxge_var.h:65-66) filled from device ring sizes (:3960/:3194-3196), so entries >= 32768 wraps the mask negative (:3007-3009). The file already validates the adopt-path offset at :867 - the pattern exists, these loci lack it. Guest has no Myricom device; not runtime-testable.
No comments yet.