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

ata_ati_chipinit AHCI fallback attaches with dangling ctlr->r_res2: double bus_release_resource (rman UAF/panic) at detach

Summary

The AHCI reset-timeout error path releases r_res2 without NULLing it, and the ATI fallback treats ENXIO as attach-anyway - detach later double-releases the freed rman resource. The DF-2094 mirror shape (error-path lifetime). Hardware-gated (no 0x1002 on guest). NULL-on-release + ==0-success-test fix (git-apply verified).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5158 Β· 6 files
FileTypeDescriptionSize
README.md β€” 746 B ↓ raw
VERDICT.md β€” 3.9 KB ↓ raw
env.txt β€” 1.1 KB view raw
fix.diff β€” 1.1 KB view raw
verdict.json β€” 3.7 KB view raw
manifest.json β€” 1.1 KB view raw

DF-5158 β€” build/run

No PoC program exists for this finding: the vulnerable path requires an ATI (0x1002) IXP/SB6xx-8xx SATA controller in AHCI progif whose AHCI reset times out, plus a driver detach β€” none of which the QEMU audit guest can provide (guest has Intel PIIX3 IDE only; see env.txt). The defect is a statically proven error-path resource-lifetime bug, not a user-triggerable memory-safety primitive.

  • Expected (on matching hardware): attach succeeds after "AHCI controller reset failure" console message; later kldunload/unplug β†’ panic in rman_release_resource (double release).
  • Artifacts: VERDICT.md (full narrative + path:line proof), fix.diff (git-apply-able), env.txt (guest PCI/uname evidence), verdict.json.
VERDICT.md
↓ download raw

DF-5158 β€” ata_ati_chipinit AHCI fallback leaves dangling ctlr->r_res2 β†’ double bus_release_resource at detach

File: sys/dev/disk/nata/chipsets/ata-ati.c (pass 2, GLM 5.3)

What

ata_ati_chipinit() (ata-ati.c:94-120) has an AHCI fast path for ATI SB6xx/7xx/8xx parts programmed as AHCI (subclass SATA + progif AHCI 1.0). It calls ata_ahci_chipinit(dev) and treats ENXIO as "fall back to compat IDE mode and attach anyway" (ata-ati.c:113). But ata_ahci_chipinit()'s error paths release ctlr->r_res2 without NULLing the pointer:

  • ata-ahci.c:80-83 BAR(5) alloc failure β†’ r_res2 = NULL (clean), return ENXIO.
  • ata-ahci.c:85-90 interrupt setup failure β†’ releases r_res2, pointer left dangling, return ENXIO. (Unreachable via ATI: ata_setup_interrupt already succeeded at ata-ati.c:99.)
  • ata-ahci.c:94-97 AHCI controller reset timeout (ata_ahci_ctlr_reset returns ENXIO after 1000 ms, ata-ahci.c:135-141) β†’ releases r_res2, pointer left dangling, return ENXIO. Reachable via ATI.

On the reachable path (reset timeout), ata_ati_chipinit falls through to ctlr->setmode = ata_ati_setmode; return 0; (ata-ati.c:118-119) β€” attach succeeds with a stale, already-freed ctlr->r_res2 in the softc.

The only later dereference is ata_pci_detach() (ata-pci.c:267-269):

if (ctlr->r_res2) {
        bus_release_resource(dev, ctlr->r_type2, ctlr->r_rid2, ctlr->r_res2);
        ctlr->r_res2 = NULL;
}

β†’ double release of an rman resource that was already freed β†’ rman list corruption / use-after-free β†’ kernel panic on detach (kldunload / PCI hot-unplug / driver re-init).

Secondary defect on the same line: if (ata_ahci_chipinit(dev) != ENXIO) return 0; treats any future non-ENXIO error return as success. Today ata_ahci_chipinit returns only {0, ENXIO}, so the live bug is the stale-pointer one.

Trigger preconditions

  1. ATI/AMD SB600/SB700/SB800-family SATA controller (vendor 0x1002, ids ATA_ATI_IXP600_S1/S2, IXP700_S1..S4, IXP800_S1/S2) programmed by BIOS with subclass PCIS_STORAGE_SATA + progif PCIP_STORAGE_SATA_AHCI_1_0.
  2. ata_ahci_ctlr_reset() times out (GHC_HR never clears within 1 s) β€” i.e. a wedged/faulty controller or a pre-reset register state that sticks.
  3. Driver detach afterwards (module unload / hot-unplug) β€” privileged action.

Net: privileged local DoS on flaky hardware; no info leak, no arbitrary write. Severity Low (HW-fault-gated + already-privileged trigger), CWE-459 (incomplete cleanup) shading into CWE-416 at detach.

Why untested on the guest

The audit QEMU guest has an Intel PIIX3 IDE controller (atapci0@pci0:0:1:1: chip=0x70108086) β€” no ATI 0x1002 IDE/SATA PCI ID exists on the bus, so ata_ati_ident() (ata-ati.c:66-70) never matches and the code path cannot be instantiated. Triggering additionally requires an AHCI reset timeout, which is a hardware fault state, not something QEMU's ich9/piix models produce for an ATI device class that QEMU does not emulate at all. See env.txt.

The code flow itself is statically proven above (attach-success-with-stale-pointer follows mechanically from the cited lines).

Fix

fix.diff (in this directory) β€” two hunks: 1. ata-ahci.c: NULL ctlr->r_res2 at both release-on-error sites (root cause; fixes every vendored caller of ata_ahci_chipinit, not just ATI). 2. ata-ati.c: treat only == 0 as AHCI success so any non-zero error falls back to compat mode instead of masquerading as success.

fix_status: not_testable β€” no ATI hardware on the guest (see env.txt); the diff is line-accurate against the audited tree but could not be boot-validated.

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 is line-accurate (git apply --check passes against a clean sys/ checkout) and mechanically removes the dangling pointer at both ata-ahci.c error paths plus tightens the ATI success test to ==0, but no baseline could be reproduced on this guest (no ATI hardware), so a patched-kernel boot validation was not possible.

fix.diff in this directory; verification of applicability: 'git apply --check' => APPLIES CLEAN against sys/.
↓ fix.diffper-fix-DF-5158

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (full path:line proof); env.txt (guest pciconf: atapci0 chip=0x70108086 Intel PIIX3, no 0x1002 device => path unreachable on guest); fix.diff (git-apply-able, verified with 'git apply --check' against a clean checkout of sys/) β€” decisive lines: ata-ahci.c:94-97 release-without-NULL, ata-ati.c:108-119 ENXIO-means-fallback-then-return-0, ata-pci.c:267-269 detach double-release.

PoC changes

No PoC exists: the finding is hardware-gated (requires an ATI IXP/SB SATA controller in AHCI mode whose reset times out, then a detach). The audit guest has no vendor-0x1002 PCI device, so the driver never attaches.

Verified recommended fix

NULL ctlr->r_res2 at ata_ahci_chipinit's release-on-error sites and treat only ==0 as AHCI success in ata_ati_chipinit (see fix.diff).

Verdict

Statically proven error-path resource-lifetime bug, not demonstrable on the audit guest. If an ATI SB6xx/7xx/8xx SATA controller is programmed as AHCI (subclass SATA, progif AHCI 1.0) and its AHCI reset times out, ata_ahci_chipinit() releases ctlr->r_res2 without NULLing it (ata-ahci.c:94-97) and returns ENXIO; ata_ati_chipinit() treats ENXIO as 'fall back to compat IDE and attach anyway' (ata-ati.c:113,118-119), so attach succeeds with a stale already-freed resource pointer in the softc, and ata_pci_detach() later double-releases it (ata-pci.c:267-269) -> rman use-after-free/panic. The QEMU guest has only an Intel PIIX3 IDE controller (chip=0x70108086) and no vendor-0x1002 PCI device at all (env.txt), so ata_ati_ident() can never match and no PoC can exercise the path; the trigger additionally requires a hardware fault (reset timeout) plus a privileged detach, hence Low severity and status untested rather than false_positive β€” the code flow itself is certain from the cited lines.