Firmware load: registration-metadata size drives an unbounded SRAM PIO copy (*limit initialized to sram_size then blindly overwritten) and short-inflate copies uninitialized kernel heap to the NIC
Summary
mxge_load_firmware_helper takes the claimed uncompressed size from fw->version (the FIRMWS Makefile field), kmallocs fw_len, and PIO-copies fw_len bytes to sc->sram+MXGE_FW_OFFSET without ever comparing fw_len to the *limit the caller seeded with sc->sram_size - the code simply overwrites it. A crafted/corrupted fw module claiming > sram_size-1MB writes past the pmap_mapdev BAR mapping (kernel VA corruption). inflate() returning Z_STREAM_END with total_out < fw_len leaves the kmalloc'd tail uninitialized yet still shipped to the NIC; version==NULL gives kmalloc(0) + a 4-byte read before any check. The zlib-stream validation and header walk are sound - DF-3573/3631 firmware-trust family, hardening-grade (root-supplied input).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3719 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.9 KB | β raw | |
| VERDICT.md | β | 1.5 KB | β raw | |
| fix.diff | β | 613 B | view raw | |
| verdict.json | β | 2.3 KB | view raw | |
| manifest.json | β | 846 B | view raw | |
| env.txt | β | 583 B | view raw |
DF-3719 β mxge firmware image load: PIO copy unbounded vs the SRAM mapping; short-inflate copies uninitialized heap to the device
Vulnerability (firmware-file trust family, DF-3573/DF-3631)
sys/dev/netif/mxge/if_mxge.c mxge_load_firmware_helper() (:651-729):
- Unbounded SRAM write. The claimed uncompressed size is taken from
the firmware registration metadata β
fw_len = (size_t)fw->version(:685), the third field ofFIRMWS = <file>:<name>:<uncompressed-size>in sys/dev/netif/mxge/fw//Makefile (e.g. mxge_eth_z8e:377284). A buffer offw_lenbytes is kmalloc'd (:686) and, after a header check (:699-705), the wholefw_lenregion is copied to the NIC viamxge_pio_copy(sc->sram + MXGE_FW_OFFSET + i, ...)(:713-719). Nothing relatesfw_lento the SRAM window*:mxge_load_firmwarepassessize = sc->sram_sizeas*limit(:910-911) precisely so the helper could check β but the helper simply overwrites it with*limit = fw_len(:721) without ever comparing. A crafted/mismatched firmware module with claimed size >sram_size - MXGE_FW_OFFSET(~917 KB of usable space behind offset 1 MB in the ~1.87 MB mapping, :4290) PIO- writes past the end of thepmap_mapdevBAR mapping β kernel VA corruption. - Uninitialized heap to the device.
inflate()may returnZ_STREAM_ENDwithzs.total_out < fw_len(compressed stream shorter than the registered size). The copy loop (:713) still copiesfw_lenbytes from the kmalloc'd (uninitialized) buffer β up tofw_len - total_outbytes of kernel heap contents are written into NIC SRAM. - (Edge) a fw module registered with
version == NULLyieldsfw_len == 0:kmalloc(0)then*(uint32_t *)(inflate_buffer + MCP_HEADER_PTR_OFFSET)(:700) reads 4 bytes at +0x3c out of a zero-sized allocation before any check runs.
The zlib stream itself is validated (!= Z_STREAM_END β EIO, :692-696)
and the header offset is bounds-checked (hdr_offset & 3 or
hdr_offset + sizeof(*hdr) > fw_len, :701) β so the parse is sound; the
missing piece is the size relationship to *limit and the true inflated
length.
Threat model
Firmware images are kernel modules built from sys/contrib/dev/mxge/.fw.uu
(DF-3573/DF-3631 family): loading one requires root (kldload), so this is
a root-supplied-input hardening gap (Low), not an unprivileged attack β
but it is also the guard against a corrupted* shipped image (build-box
compromise, truncated file where metadata and payload disagree).
Verification
Hardware-gated: mxge_load_firmware_helper runs at attach (:4341 via
mxge_select_firmware) β the guest has no Myricom device. Source-certain;
inconclusive per family convention.
Fix
fix.diff: clamp fw_len to zs.total_out after a successful inflate,
and reject images that don't fit *limit - MXGE_FW_OFFSET before the copy
loop (git-apply clean).
DF-3719 VERDICT
Status: inconclusive (hardware-gated; attach-time path needs a Myricom NIC), confidence: certain source-level.
Source proof
- fw_len comes from fw registration metadata, not the stream: :685
fw_len = (size_t)fw->version;(FIRMWS third field, fw/*/Makefile). - Copy loop :713-719 writes fw_len bytes at sc->sram + MXGE_FW_OFFSET with no comparison to sc->sram_size even though the caller seeded limit = sc->sram_size (:910-911); :721 overwrites limit with fw_len.
- Short-inflate: only
status != Z_STREAM_ENDis rejected (:692); a stream that ends early leaves inflate_buffer's tail uninitialized and still copied for fw_len bytes. - version==NULL edge β kmalloc(0) + 4-byte read at +0x3c before any bounds check (:700).
Zlib-stream validation and the header walk are correct β this finding is strictly the missing fw_lenβSRAM and fw_lenβtotal_out relationships.
Not reproduced
Attach path requires the device (firmware_get runs from mxge_select_firmware at attach :4341). Guest: no 0x14c1 device (env.txt).
Fix
fix.diff: fw_len = min(fw_len, zs.total_out) + reject fw_len+MXGE_FW_OFFSET
*limit. git apply --check clean; not buildable without hardware.
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-3719/fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
findings/poc/DF-3719/VERDICT.md; fix.diff (total_out clamp + SRAM fit check, git-apply clean); env.txt
PoC changes
No seed PoC; hardware-gated.
Verified recommended fix
Clamp fw_len to zs.total_out after successful inflate and reject fw_len + MXGE_FW_OFFSET > *limit before the PIO copy loop.
Verdict
Source-certain, hardware-gated (attach-time path; DF-3573/DF-3631 firmware-file trust family): mxge_load_firmware_helper (if_mxge.c:651-729) takes the claimed uncompressed size from firmware registration metadata (:685 fw->version, the FIRMWS Makefile field), PIO-copies fw_len bytes to sc->sram+MXGE_FW_OFFSET (:713-719) without ever comparing fw_len to the *limit the caller seeded with sc->sram_size (:910-911, overwritten at :721) - a crafted/corrupted image claims > sram_size-1MB and the copy walks off the pmap_mapdev BAR mapping; additionally a zlib stream that legitimately ends short of fw_len (inflate returns Z_STREAM_END with total_out < fw_len) leaves the kmalloc'd tail uninitialized yet still copied to the NIC, and a NULL version (fw_len=0) reads 4 bytes at +0x3c of a zero-sized allocation (:700). Root-supplied input (kldload), so hardening-grade (Low), same as DF-3573/DF-3631.
No comments yet.