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

ata_marvell_edma_allocate() dereferences ch->dma unconditionally (work_bus/work/work_tag): NULL-pointer panic when ata_dmaalloc() failed at channel attach β€” DF-4809 class, but in ->allocate (marvell is the only nata chipset whose allocate touches ch->dma)

Summary

ata_dmaalloc failure NULLs ch->dma (the framework supports PIO-only channels) but ata_pcichannel_attach calls ctlr->allocate unconditionally - marvell's EDMA allocate immediately reads ch->dma->work_bus. Attach-time NULL panic on 88SX EDMA under DMA allocation failure; live code (#included into natapci). No 88SX under QEMU - complete line-trace proof + fix compile-validated on guest (natapci.ko -Werror clean). Guard fix (DF-4809 methodology).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4813 Β· 5 files
FileTypeDescriptionSize
README.md β€” 1.4 KB ↓ raw
VERDICT.md β€” 4.3 KB ↓ raw
verdict.json β€” 3.2 KB view raw
fix.diff β€” 381 B view raw
fix_build.log β€” 9.9 KB view raw

DF-4813 β€” nata ata-marvell (EDMA): ata_marvell_edma_allocate() dereferences ch->dma unconditionally β€” NULL-pointer panic when ata_dmaalloc() failed at channel attach (DF-4809 class, in ->allocate instead of ->reset).

No runnable trigger exists on the audit guest (QEMU has no 88SX emulation and the PCI device cannot be faked the way an ACPI table can), so this pack holds the complete static proof plus a compile-validation of the fix, mirroring the accepted DF-4809 methodology.

Contents

VERDICT.md full path:line trace: ata-pci.c:795-798 (unconditional ctlr->allocate after ch->dma may be NULL) -> ata-dma.c:149-153 (ata_dmaalloc failure NULLs ch->dma) -> ata-marvell.c:209 (work = ch->dma->work_bus;). fix.diff 2-line guard (if (ch->dma == NULL) return ENXIO;) fix_build.log natapci.ko rebuilt clean (-Werror) on the audit guest with the guard applied: build_rc=0.

Build (fix compile-validation, on the guest)

cd /usr/src && patch -p1 < fix.diff cd /usr/src/sys/dev/disk/nata/natapci && make obj && make

Expected

Static: on any 88SX50xx/60xx/6042/7042 EDMA system whose channel attach hits an ata_dmaalloc() failure, the unpatched driver panics in ata_marvell_edma_allocate (ch->dma == NULL); with the guard the channel attach fails cleanly with ENXIO (handled at ata-pci.c:798-801).

VERDICT.md
↓ download raw

DF-4813 β€” VERDICT

Status: NOT DYNAMICALLY TESTABLE on the audit guest (no Marvell 88SX PCI device exists under QEMU). Static proof is complete and unambiguous: same class as the accepted DF-4809 (ata-ahci).

Root cause β€” full path trace

ata_marvell_edma_allocate() dereferences ch->dma without a NULL check, while its only caller invokes it after the DMA setup may have failed and NULLed ch->dma:

  1. ata_pcichannel_attach() β€” sys/dev/disk/nata/ata-pci.c:787-805: c 793 if (ctlr->dmainit) 794 ctlr->dmainit(dev); /* ata_marvell_edma_dmainit -> ata_dmainit: ch->dma = kmalloc(...M_INTWAIT) */ 795 if (ch->dma) 796 ch->dma->alloc(dev); /* ata_dmaalloc β€” CAN FAIL */ 797 798 if ((error = ctlr->allocate(dev))) { /* called UNCONDITIONALLY */ 799 if (ch->dma) 800 ch->dma->free(dev); 801 return error; 802 }
  2. ata_dmaalloc() β€” sys/dev/disk/nata/ata-dma.c:91-154: any of the five bus_dma_tag_create calls, either bus_dmamem_alloc (SG table page / 8 KB work area), either bus_dmamap_load, or bus_dmamap_create failing prints "WARNING - DMA allocation failed, disabling DMA" and executes c 152 kfree(ch->dma, M_ATADMA); 153 ch->dma = NULL; The channel attach then continues (the framework explicitly supports PIO-only operation β€” that is why 795/799 guard with if (ch->dma)).
  3. ata_marvell_edma_allocate() β€” sys/dev/disk/nata/chipsets/ata-marvell.c: c 209 work = ch->dma->work_bus; <-- NULL deref (panic #1) 211 bzero(ch->dma->work, 1024+256); 212 bus_dmamap_sync(ch->dma->work_tag, ch->dma->work_map, ...); Every other ch->dma consumer in the framework guards with if (ch->dma) (ata-pci.c:795, 799, 816, 843); ata_marvell_pata_allocate (the 61xx PATA path) correctly goes through ata_pci_allocate which never touches ch->dma. Among ALL nata chipsets, marvell-edma is the only ->allocate implementation that dereferences ch->dma (ahci's allocate β€” ata-ahci.c: 159-182 β€” does not; its deref lived in reset, filed as DF-4809).

Reachability: the marvell EDMA glue is compiled into the live natapci module / GENERIC kernel (ata-chipset.c:159-160, `#if !defined(ATA_NO_MARVELL)

include "chipsets/ata-marvell.c"`), for all 50xx/60xx/6042/7042 EDMA parts

(ata-marvell.c:75-82).

Trigger: attach of an 88SX EDMA channel (boot-time enumeration or later module load / hot-plug) at a moment when the DMA allocations of step 2 fail (DMA-coherent memory fragmentation, dma-window constraints). Result: NULL-pointer-write/panic in the device attach path -> boot or kldload DoS (CWE-476). No attacker-controlled data; not an unprivileged primitive β€” the precondition is an environmental allocation failure.

Why not reproduced on the guest

QEMU cannot present an 88SX PCI storage controller (no emulation exists, and unlike the ACPI case of DF-4812 the PCI ID/behavior cannot be injected with -acpitable), and forcing ata_dmaalloc() failure requires the real device attach to run. Verification therefore mirrors the accepted DF-4809 methodology: line-precise static trace of the full call chain (above), plus a compile-validation of the fix on the audit guest:

# cd /usr/src && patch -p1 < fix.diff
# cd /usr/src/sys/dev/disk/nata/natapci && make obj && make   [see fix_build.log]

Fix

Guard at the top of ata_marvell_edma_allocate (fix.diff, build-validated):

+    if (ch->dma == NULL)
+   return ENXIO;
+
     work = ch->dma->work_bus;

The channel then fails attach cleanly; ata_pcichannel_attach already handles the error (ata-pci.c:798-801).

Classification

  • severity: Low (attach-time NULL-deref panic, environmental trigger; matches DF-4809)
  • CWE-476 (NULL Pointer Dereference)
  • bucket: base:dev
  • kernel_refs: chipsets/ata-marvell.c:209, chipsets/ata-marvell.c:211-213, ata-pci.c:795-798, ata-dma.c:149-153

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.diff adds the same 'if (ch->dma == NULL) return ENXIO;' guard the framework itself uses; compile-validated on the audit guest (natapci.ko rebuilt clean with -Werror). Behavioral validation impossible on QEMU: no 88SX device, and ata_dmaalloc failure cannot be produced without the real attach.

findings/poc/DF-4813/fix.diff; findings/poc/DF-4813/fix_build.log
↓ fix.diffper-fix-DF-4813

Confirmed kernel references

Detail

Evidence (decisive lines)

findings/poc/DF-4813/VERDICT.md (full path:line trace); findings/poc/DF-4813/fix.diff; findings/poc/DF-4813/fix_build.log (clean -Werror module build with the guard)

PoC changes

not applicable (no seed PoC; pass-2 static finding; dynamic trigger requires physical 88SX hardware)

Verified recommended fix

Guard ata_marvell_edma_allocate with 'if (ch->dma == NULL) return ENXIO;' before the first ch->dma dereference (ata-marvell.c:209)

Verdict

Static NULL-deref with complete path proof, not guest-runnable (no Marvell 88SX device exists under QEMU and a PCI storage controller cannot be injected like an ACPI table): ata_dmaalloc() failure leaves ch->dma == NULL (ata-dma.c:152-153) but channel attach continues (framework supports PIO-only, guarding every other ch->dma use at ata-pci.c:795/799/816/843); ata_pcichannel_attach then calls ctlr->allocate(dev) UNCONDITIONALLY (ata-pci.c:798) and ata_marvell_edma_allocate immediately dereferences ch->dma->work_bus (ata-marvell.c:209) and ch->dma->work/work_tag/work_map (211-213) -> attach/boot-time kernel panic (CWE-476) under DMA-coherent memory exhaustion on any 88SX50xx/60xx/6042/7042 EDMA system. Marvell is the only nata chipset whose ->allocate derefs ch->dma (ahci's allocate does not β€” its deref was in reset, DF-4809). Fix compile-validated on the audit guest: guard applied, natapci.ko rebuilt clean with -Werror (fix_build.log, build_rc=0).