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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3928 Β· 5 files
FileTypeDescriptionSize
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.

VERDICT.md
↓ download raw

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)

  1. mly_get_controllerinfo() (sys/dev/raid/mly/mly.c:744-780) receives the struct mly_ioctl_getcontrollerinfo blob DMA'd by the controller firmware. physical_channels_present and virtual_channels_present are u_int8_t (mlyreg.h:576-577) β€” no validation anywhere.
  2. 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_sim is struct cam_sim *mly_cam_sim[MLY_MAX_CHANNELS] with MLY_MAX_CHANNELS == 6 (mlyvar.h:206, mlyreg.h:59). With, e.g., physical=0/virtual=255 the loop stores kernel heap pointers at mly_cam_sim[6..254] β€” up to 1992 bytes past the array, overrunning mly_cam_channels, mly_task_complete, mly_qfrzn_cnt and the entire remainder of the softc allocation into adjacent slab memory. The physical loop (mly.c:1927-1941, gated on the hw.mly.register_physical_channels env var) can push writes up to index 509.
  3. sc->mly_cam_channels = physical + virtual (mly.c:1967-1968) β€” up to 510.
  4. 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_btl is [MLY_MAX_CHANNELS][MLY_MAX_TARGETS] = [6][16] (mlyvar.h:206); the walk writes sizeof(struct mly_btl)==36-strided words of value 8 across up to 510*16 = 8160 entries β‡’ ~290KB past mly_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 reads mly_cam_sim[bus] out of bounds for bus β‰₯ 6.
  5. 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_testable
baseline no→ patch + rebuild →patched clean

Fix 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
↓ fix.diffper-fix-DF-3928

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.