ata_ahci_reset() dereferences ch->dma unconditionally: NULL-pointer panic when ata_dmaalloc() failed at channel attach
Summary
ch->dma->work_bus is read with no NULL guard; ata_dmaalloc failure frees ch->dma and NULLs it but attach continues, and ata_pcichannel_reset calls ctlr->reset unguarded (while the adjacent DMA reset in the same function IS guarded). NULL+offset panic at attach/reinit/hot-plug under DMA memory exhaustion. Guard-after-port-check fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4809 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | file | 3.0 KB | β raw | |
| VERDICT.md | file | 2.0 KB | β raw | |
| fix.diff | file | 452 B | view raw | |
| verdict.json | file | 2.0 KB | view raw |
DF-4809 β ata_ahci_reset() NULL ch->dma dereference when DMA workspace alloc failed
File: sys/dev/disk/nata/chipsets/ata-ahci.c (pass 2)
Severity: Low Β· CWE-476 (NULL pointer dereference) Β· confidence: likely
(class: robustness / boot-time panic; not the DF-1891/DF-2510 unbounded-channels family)
Build / run
Not applicable β this is a static control-flow finding. There is no buildable,
runnable PoC: the trigger is ata_dmaalloc() failure (bus_dmamem_alloc /
bus_dma_tag_create failing for the 4 KB sg + 8 KB work DMA areas) during
ATA channel attach, i.e. DMA-coherent memory exhaustion at device attach time.
Verification on the QEMU guest was therefore not attempted (Phase V bar
not met: not Critical/High, not memcorrupt/privesc, no trivial local program).
The path (all citations from this tree)
ata_pcichannel_attach()βsys/dev/disk/nata/ata-pci.c:790-793:ctlr->dmainit(dev)thenif (ch->dma)ch->dma->alloc(dev). For AHCI,dmainit = ata_ahci_dmainit(set atata-ahci.c:106),alloc = ata_dmaalloc(set atata-dma.c:69).ata_dmaalloc()βsys/dev/disk/nata/ata-dma.c:90-154: any tag-create / dmamem-alloc / map-load failure jumps toerror:(line 149) which prints "WARNING - DMA allocation failed, disabling DMA",kfree(ch->dma), and setsch->dma = NULL(ata-dma.c:152-153). Attach continues β it does not abort the channel.ata_attach()βsys/dev/disk/nata/ata-all.c:111(body around 126-130):ATA_RESET(dev)is called unconditionally for every channel.ATA_RESETβata_pcichannel_reset()βsys/dev/disk/nata/ata-pci.c:841-851: the DMA-engine reset is guarded byif (ch->dma)(842-847), butctlr->reset(dev)(850-851) is called with no guard.- For AHCI,
ctlr->reset = ata_ahci_reset(set atata-ahci.c:105).ata_ahci_reset()βsys/dev/disk/nata/chipsets/ata-ahci.c:466-469:work = ch->dma->work_bus + ATA_AHCI_CL_OFFSET;β unguarded NULL deref (offset deref of a NULLstruct ata_dma *) β kernel panic.
The same reset is also reached on resume / channel reinit
(ata_reinit, ata-all.c:187 β ATA_RESET), so the NULL must also survive any
later re-init after a failed alloc.
Sibling code is uniformly guarded: ata-pci.c:791 (if (ch->dma)
alloc), ata-pci.c:811 (if (ch->dma) free in detach), ata-pci.c:842
(if (ch->dma) in this very reset function) β the AHCI reset's unconditional
ch->dma->work_bus is the outlier.
Expected behavior if triggered
Fatal trap 12: page fault in supervisor mode while accessing
(struct ata_dma *)NULL + offsetof(work_bus) during device attach / reinit β
boot-time or hot-plug-attach denial of service. No privilege impact; the
written value never reaches the fault (the fault is the first access).
Fix
Guard the work-area setup (see fix.diff):
if (ch->dma == NULL || ch->dma->work_bus == 0)
return;
placed after the port-implemented check (ata-ahci.c:461-464), mirroring the
if (ch->dma) guards used by every sibling path.
DF-4809 β VERDICT
Status: untested (static-analysis finding; Phase V not applicable β no runnable PoC exists for the trigger condition on the QEMU guest).
Reproduced?
No, and deliberately not attempted. Triggering requires ata_dmaalloc()
(ata-dma.c:90-154) to fail for an AHCI channel at attach time, which requires
bus_dmamem_alloc/bus_dma_tag_create failure for 12 KB of DMA-coherent
memory β i.e. DMA-mappable memory exhaustion during device enumeration. There
is no unprivileged (or any) syscall path to force this from a running system;
it is a boot/hot-plug-time robustness defect. Per the audit contract
(Low/Info findings do not get guest verification) the QEMU guest was not used
for this finding.
Why I am confident the bug is real anyway
The code path is fully static and unconditional:
ch->dma = NULLis the documented failure exit ofata_dmaalloc(ata-dma.c:152-153) and attach proceeds afterwards.ata_attachβATA_RESET(dev)βata_pcichannel_resetcallsctlr->reset(dev)with no NULL guard (ata-pci.c:850-851) while guarding the adjacent DMA reset withif (ch->dma)(ata-pci.c:842).ata_ahci_resetimmediately doesch->dma->work_bus(ata-ahci.c:467).
Impact ceiling: panic at attach/reinit (CWE-476). Not memory corruption, no attacker-controlled data is involved; severity Low, confidence "likely" (path certain; trigger constrained to resource exhaustion).
Recommended fix
One-line guard in ata_ahci_reset (see fix.diff in this directory; authored
against the read-only sys/ tree, never applied):
if (ch->dma == NULL || ch->dma->work_bus == 0)
return;
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 adds the same 'if (ch->dma == NULL) return;' guard used by sibling paths; not build-validated because the trigger condition (DMA alloc failure) cannot be produced on the guest
findings/poc/DF-4809/fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
findings/poc/DF-4809/VERDICT.md (full path trace with path:line citations); findings/poc/DF-4809/fix.diff (one-line guard)
PoC changes
not applicable (no seed PoC; finding produced by pass-2 static analysis)
Verified recommended fix
Guard ata_ahci_reset work-area setup with 'if (ch->dma == NULL || ch->dma->work_bus == 0) return;' after the port-implemented check
Verdict
Static NULL-deref, not guest-verified (no runnable trigger): ata_dmaalloc() failure leaves ch->dma == NULL (ata-dma.c:152-153) but channel attach continues; ata_attach -> ATA_RESET -> ata_pcichannel_reset (ata-pci.c:850-851) invokes ctlr->reset with no NULL guard, and ata_ahci_reset immediately dereferences ch->dma->work_bus (ata-ahci.c:466-469) -> attach/reinit-time kernel panic (CWE-476) under DMA-coherent memory exhaustion. Every sibling path guards with 'if (ch->dma)' (ata-pci.c:791, 811, 842). Severity Low: boot/hot-plug DoS only, no attacker-controlled data.
No comments yet.