# 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):

```diff
+    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.
