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

Unchecked NULL from ahci_ata_get_xfer() in ahci_set_feature() and ahci_read_log() (ahci.c instances of the DF-2420/DF-2423 class) -> kernel panic

Summary

ahci_set_feature() and ahci_read_log() dereference ahci_ata_get_xfer()'s return without a NULL check; it returns NULL when the per-port free-CCB list is empty. The pool is sc_ncmds-1 deep and CAM's devq has exactly sc_ncmds-1 openings, so concurrent I/O can hold every free CCB; xa->fis (first struct member) then faults on NULL. Same class as DF-2420 (ahci_cam.c)/DF-2423 (ahci_pm.c) - these are the two ahci.c sites neither record covers. ahci_read_log reachable from DevSleep probe during re-probe concurrent with I/O saturation (hotplug-conditioned); ahci_set_feature from the root-gated link_pwr_mgmt sysctl and DIPM negotiation. NULL-deref panic.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3774 Β· 5 files
FileTypeDescriptionSize
README.md β€” 2.4 KB ↓ raw
VERDICT.md β€” 2.0 KB ↓ raw
env.txt β€” 551 B view raw
fix.diff β€” 722 B view raw
verdict.json β€” 1.9 KB view raw

DF-3774 β€” ahci_set_feature() / ahci_read_log(): unchecked NULL from ahci_ata_get_xfer() β†’ NULL-pointer kernel panic under CCB-pool exhaustion

File: sys/dev/disk/ahci/ahci.c (pass 2, GLM 5.3)

What the bug is

ahci_ata_get_xfer() (ahci.c:3669-3689) returns NULL when the per-port CCB free list is empty (ahci_get_ccb() β†’ NULL). Two ahci.c callers never check:

int
ahci_set_feature(struct ahci_port *ap, struct ata_port *atx, ...)
{
    ...
    xa = ahci_ata_get_xfer(ap, atx);     /* ahci.c:4057 */
    xa->fis->type = ATA_FIS_TYPE_H2D;    /* ahci.c:4059 β€” NULL deref */
    ...
}

int
ahci_read_log(struct ahci_port *ap, struct ata_port *atx, ...)
{
    ...
    xa = ahci_ata_get_xfer(ap, atx);     /* ahci.c:4090 */
    xa->fis->type = ATA_FIS_TYPE_H2D;    /* ahci.c:4092 β€” NULL deref */
    ...
}

fis is the first member of struct ata_xfer (atascsi.h:356-358), so the fault is a read of *(NULL + 0) β†’ kernel panic.

This is the same defect class already filed for the sibling files: DF-2420 (ahci_cam.c dispatch sites) and DF-2423 (ahci_pm_read/write). These two ahci.c sites were not covered by either record.

Reachability

The CCB pool is sc_ncmds - 1 deep (slot 1 is the reserved error CCB, ahci.c:431-439) and CAM's devq is sized to exactly sc_ncmds - 1 openings (ahci_cam.c:111-115), so ordinary CAM I/O alone can hold every free CCB β€” in particular CCBs in the window where ahci_xpt_scsi_*_io() has taken a CCB (ahci_cam.c:1082/1524, under ap_sim_lock only) but is blocked in ahci_ata_cmd() on the port lock.

  • ahci_read_log is called from ahci_cam_probe_disk() (ahci_cam.c:605, DevSleep identify support) during (re)probe, which runs while the port lock is held for seconds (PM probe sleeps 5s) β€” CAM submissions from other CPUs can hold all free CCBs in that window β†’ NULL deref.
  • ahci_set_feature is called from ahci_port_link_pwr_mgmt() (ahci.c:634, 641, 688, 695, 718, 721 β€” sysctl hw.ahci.X.link_pwr_mgmt, root-gated) and from DIPM/APST negotiation.

Impact: NULL-deref panic (local DoS). Combined severity capped at Low for this file's sites (probe-path variant is hotplug/PM-conditioned; sysctl variant is root-gated) β€” see DF-2420/2423 for the class discussion.

Why not reproduced on the guest

No AHCI controller on the audit guest (env.txt); the probe/link-pm paths cannot execute. Class precedent (DF-2420) documents the exhaustion mechanics.

VERDICT.md
↓ download raw

DF-3774 VERDICT β€” not_reproduced (no AHCI hardware on guest); NULL-deref sites certain by source

Bottom line

ahci_set_feature() (ahci.c:4047-4077) and ahci_read_log() (ahci.c:4080-4115) call ahci_ata_get_xfer() (ahci.c:3669-3689) and immediately dereference the result (xa->fis->type = ... at ahci.c:4059 and ahci.c:4092) without a NULL check. ahci_ata_get_xfer() returns NULL whenever the per-port free-CCB list is empty. fis is the first field of struct ata_xfer (atascsi.h:356-358), so a NULL xa faults on the next line β€” kernel panic.

Pool-exhaustion is reachable because CAM's devq has exactly sc_ncmds - 1 openings (ahci_cam.c:111-115) matching the sc_ncmds - 1 free CCBs (slot 1 reserved, ahci.c:431-439): a burst of submissions grabs all free CCBs under ap_sim_lock (ahci_cam.c:1082/1524) and holds them while blocked on the port lock. ahci_read_log runs from ahci_cam_probe_disk() (ahci_cam.c:605) during probe β€” which holds the port lock for seconds during PM probing β€” and ahci_set_feature runs from the hw.ahci.X.link_pwr_mgmt sysctl (ahci_dragonfly.c:150-162 β†’ ahci.c:595-752) and DIPM negotiation.

Same class as DF-2420 (ahci_cam.c) and DF-2423 (ahci_pm.c); those records cover their files' sites, these are the two unfiled ahci.c sites.

Not-reproduced rationale

Guest has no AHCI controller (env.txt) β€” neither probe nor link-pm paths can run. NULL-deref on exhaustion is certain from source; runtime frequency depends on I/O concurrency (documented for the class in DF-2420).

Check for NULL and return EBUSSE/EIO:

    xa = ahci_ata_get_xfer(ap, atx);
    if (xa == NULL)
        return (EBUSY);     /* or EIO for read_log */

see fix.diff.

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

Two-line guard authored; cannot be exercised without AHCI hardware.

fix.diff in this pack.
↓ fix.diffper-fix-DF-3774

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md; ahci.c:4057+4059 and ahci.c:4090+4092 (unchecked deref); ahci.c:3674-3679 (get_xfer NULL path); ahci.h/atascsi.h:356-358 (fis is first member); ahci_cam.c:111-115 (devq openings == pool depth)

PoC changes

No runtime PoC - no AHCI hardware on guest; class mechanics documented in DF-2420/DF-2423.

Verified recommended fix

NULL-check the ahci_ata_get_xfer() return in ahci_set_feature and ahci_read_log and return EBUSY.

Verdict

ahci_set_feature() (ahci.c:4057/4059) and ahci_read_log() (ahci.c:4090/4092) dereference the return of ahci_ata_get_xfer() without a NULL check; the function returns NULL when the per-port CCB free list is empty (sc_ncmds-1 slots, all holdable by concurrent CAM submissions whose devq has exactly that many openings, ahci_cam.c:111-115). NULL xa faults on xa->fis (first struct member) - kernel panic. ahci_read_log is reached from DevSleep probe (ahci_cam.c:605) and ahci_set_feature from the root-gated link_pwr_mgmt sysctl and DIPM negotiation. Same defect class as DF-2420/DF-2423; these are the ahci.c sites neither record covers. Not reproduced: guest has no AHCI controller.