Unvalidated firmware channel counts overflow mly_cam_sim[6] and mly_btl[6][16] at attach (~4KB pointer writes + ~290KB strided flag writes past the softc)
Summary
GETCONTROLLERINFO's physical+virtual channel counts (u_int8_t each) are never validated; sums up to 510 index mly_cam_sim[6] and mly_btl[6][16]. Attach-time heap corruption: cam_sim pointers written past the array (~4KB past softc), then scan_devices writes the constant 8 every 36 bytes across ~290KB past mly_btl. Malicious or firmware-corrupted Mylex card - same trust boundary as DF-1290, but attach-time, larger, pointer-valued; no user action needed. Validate-counts fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3928 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.1 KB | β raw | |
| VERDICT.md | β | 3.5 KB | β raw | |
| fix.diff | β | 1.0 KB | view raw | |
| env.txt | β | 543 B | view raw | |
| verdict.json | β | 2.8 KB | view raw |
DF-3928 β mly: unvalidated firmware channel counts overflow mly_cam_sim[6] / mly_btl[6][16]
Build / Run
No userland PoC exists for this defect: it is triggered by the controller's
answer to MDACIOCTL_GETCONTROLLERINFO during mly_attach(). On a machine
with a (malicious or firmware-corrupted) Mylex eXtremeRAID/AcceleRAID card it
fires with no user action at driver attach (boot or kldload mly.ko).
To observe with controlled firmware (lab): patch a QEMU device model to answer
GETCONTROLLERINFO with virtual_channels_present = 255 and boot the guest β
mly_cam_attach() writes mly_cam_sim[7..509] (out of bounds) and
mly_scan_devices() writes MLY_BTL_RESCAN across ~290KB past mly_btl.
Expected result
Massive OOB write of kernel pointers (cam_sim alloc results) past the softc (4KB+) and of the constant 8 (mb_flags) every 36 bytes across ~290KB, corrupting the heap after the softc; attach proceeds to CAM registration with garbage; near-certain panic, groomable into exploitable corruption.
The audit guest has no mly hardware, so this is source-level certain but not runtime-reproduced. See VERDICT.md.
DF-3928 verdict narrative
Reproduced?
No (hardware-gated: no Mylex controller in the audit guest). Source-level certainty: certain β every link in the chain is static, with no dependency on runtime state.
Root cause chain (path:line)
mly_get_controllerinfo()(sys/dev/raid/mly/mly.c:744-780) receives thestruct mly_ioctl_getcontrollerinfoblob DMA'd by the controller firmware.physical_channels_presentandvirtual_channels_presentareu_int8_t(mlyreg.h:576-577) β no validation anywhere.mly_cam_attach()(mly.c:1947-1961):c chn = sc->mly_controllerinfo->physical_channels_present; /* 0..255 */ for (i = 0; i < sc->mly_controllerinfo->virtual_channels_present; i++, chn++) { if ((sc->mly_cam_sim[chn] = cam_sim_alloc(...)) == NULL) {mly_cam_simisstruct cam_sim *mly_cam_sim[MLY_MAX_CHANNELS]withMLY_MAX_CHANNELS == 6(mlyvar.h:206, mlyreg.h:59). With, e.g., physical=0/virtual=255 the loop stores kernel heap pointers atmly_cam_sim[6..254]β up to 1992 bytes past the array, overrunningmly_cam_channels,mly_task_complete,mly_qfrzn_cntand the entire remainder of the softc allocation into adjacent slab memory. The physical loop (mly.c:1927-1941, gated on thehw.mly.register_physical_channelsenv var) can push writes up to index 509.sc->mly_cam_channels = physical + virtual(mly.c:1967-1968) β up to 510.mly_scan_devices()(mly.c:802-805):c for (bus = 0; bus < sc->mly_cam_channels; bus++) /* 0..509 */ if (MLY_BUS_IS_VALID(sc, bus)) /* reads mly_cam_sim[bus] OOB */ for (target = 0; target < MLY_MAX_TARGETS; target++) sc->mly_btl[bus][target].mb_flags = MLY_BTL_RESCAN;mly_btlis[MLY_MAX_CHANNELS][MLY_MAX_TARGETS]= [6][16] (mlyvar.h:206); the walk writessizeof(struct mly_btl)==36-strided words of value 8 across up to 510*16 = 8160 entries β ~290KB pastmly_btl, i.e. far beyond the ~30KB softc allocation.mly_periodic()(mly.c:1378-1391) repeats the same walk every second;MLY_BUS_IS_VALID(mlyvar.h:286) itself readsmly_cam_sim[bus]out of bounds for bus β₯ 6.mly_rescan_btl()(mly.c:817-868) then kfree/malloc/kmalloc-DMAs based on those OOB'd arrays.
Threat model
Malicious or buggy PCIe controller (DMA'd firmware response), identical trust boundary to the accepted DF-1290 (event channel/target/lun OOB write) β but larger and at attach, and including pointer-valued writes (cam_sim pointers).
Impact
Kernel heap corruption at attach: near-certain panic; with a groomed heap the ~4KB of kernel-pointer writes plus 290KB of strided flag writes are usable corruption primitives (write-pointer-values / write-constant-8 patterns).
Why not runtime-reproduced here
No mly controller exists in the audit guest (pciconf -l shows virtio only),
and there is no software path to make the driver fetch controllerinfo from
anything but the card. Per the pass-1 family convention (DF-1289/DF-1290)
this is filed as not_reproduced with certain source confidence.
Fix
Validate the sums against MLY_MAX_CHANNELS right after GETCONTROLLERINFO β see fix.diff (returns EIO; attach fails cleanly).
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 not validated on the guest: the trigger path requires mly/age hardware absent from the audit guest. fix.diff is mechanical (bounds/termination-only) and authored post-verification against the cited lines.
findings/poc/DF-3928/fix.diff + VERDICT.md
Confirmed kernel references
Detail
Exploit chain
malicious/buggy controller answers GETCONTROLLERINFO with channel counts > 6 -> attach-time kernel heap corruption (pointer writes + 290KB strided flag writes) -> panic or groomed corruption
Evidence (decisive lines)
findings/poc/DF-3928/VERDICT.md (line-cited chain); env.txt (no mly hardware in guest); fix.diff (channel-count validation)
PoC changes
No seed PoC; hardware-gated attach-time defect - evidence pack documents the exact lab reproduction (QEMU device-model answering GETCONTROLLERINFO with virtual_channels_present=255).
Verified recommended fix
Validate physical+virtual channel counts against MLY_MAX_CHANNELS right after GETCONTROLLERINFO and fail attach (EIO).
Verdict
Hardware-gated (no Mylex controller in the audit guest); source-certain. mly_get_controllerinfo (mly.c:744-780) trusts the firmware-DMA'd u_int8_t channel counts; mly_cam_attach (mly.c:1947-1961) stores cam_sim pointers into mly_cam_sim[6] for chn up to 509 (~4KB of kernel-pointer writes past the softc) and mly_scan_devices/mly_periodic (mly.c:802-805, 1378-1391) write MLY_BTL_RESCAN across mly_btl[6][16] for bus < mly_cam_channels <= 510 (~290KB strided OOB write past the softc into the heap). Same trust boundary as accepted DF-1290 but attach-time, larger, and pointer-valued. Verified guest has no mly device (virtio-only PCI list), so no runtime trigger exists; per pass-1 family convention (DF-1289/DF-1290) filed not_reproduced with certain source confidence.
No comments yet.