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

OOB read in DHRS error path: device-controlled CCS slot index dereferenced before bounds check

Summary

ahci_port_intr() DHRS branch at ahci.c:2843-2851: err_slot=AHCI_PREG_CMD_CCS(ahci_pread) is 5-bit (0-31), but ap_ccbs[] allocated for sc_ncmds (4-32). ccb=&ap->ap_ccbs[err_slot] at :2847 BEFORE bounds check. Then ccb->ccb_xa.fis->command derefs OOB ccb then garbage fis pointer -> kernel panic. TFES path at :2724 correctly checks err_slot<ncmds first, DHRS path does NOT. Malicious/buggy AHCI controller (PCIe passthrough/Thunderbolt) sets CCS>=ncmds on DHRS error. Fix: add bounds check err_slot>=sc_ncmds before ap_ccbs indexing.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1210 Β· 9 files
FileTypeDescriptionSize
VERDICT.md verdict full path:line trace + reachability analysis 3.9 KB ↓ raw
README.md readme claim, verdict, threat model, fix 2.0 KB ↓ raw
fix.diff suggested-fix bounds-check DHRS err_slot vs sc_ncmds before ap_ccbs[] deref (mirrors TFES guard) 583 B view raw
run.sh reachability-probe shell probe: is an AHCI controller attached? 798 B view raw
build.sh build-noop no userspace build (trigger is AHCI CCS hardware register) 281 B view raw
fix_build.log build-log GENERIC kernel build with the fix applied (rc=0; ahci.o/ahci.ko clean) 5.6 MB ↓ download
env.txt environment guest uname, cc, device topology 718 B view raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme claim, verdict, threat model, fix
↓ download raw

DF-1210 β€” OOB read in AHCI DHRS error path (device-controlled CCS slot)

Claim

ahci_port_intr() DHRS branch (sys/dev/disk/ahci/ahci.c:2843-2851): err_slot = AHCI_PREG_CMD_CCS(ahci_pread(...)) is a 5-bit value (0-31) read from the AHCI controller, but ap->ap_ccbs[] is sized for sc->sc_ncmds (4-32). The code does ccb = &ap->ap_ccbs[err_slot] (ahci.c:2847) and derefs ccb->ccb_xa.fis->command (ahci.c:2850) before any bounds check. The TFES path (ahci.c:2724) correctly checks err_slot < 0 || err_slot >= sc_ncmds first; the DHRS path does not. A malicious/buggy AHCI controller returning a CCS >= sc_ncms yields an OOB ap_ccbs[] index then a deref of a garbage fis pointer β†’ kernel panic / controlled memory access.

Verdict

NOT TESTABLE on this audit guest (real bug, traced line-by-line; fix authored and compile-validated into the GENERIC kernel). See VERDICT.md.

Why not reproduced here

  • The guest has no AHCI/SATA controller: pciconf -l shows none; the disk is virtio-blk (/dev/vbd0). ahci(4) did not attach, so ahci_port_intr() is never called.
  • Even with an AHCI controller present, the malicious value comes from the controller's CCS hardware register, not from any userspace syscall. The benign QEMU AHCI model always reports a valid CCS, so there is no unprivileged path to drive err_slot >= sc_ncmds.

Trigger (threat model)

A malicious or faulty AHCI controller (PCIe peripheral, Thunderbolt storage-bridge, or a malicious VM host's AHCI device model attacking a guest) that raises a DHRS interrupt with ERR set and CCS >= sc_ncmds. Common in the real world via buggy controllers and is a class of DMA/peripheral attack surface.

Fix

fix.diff mirrors the TFES bounds check (err_slot < 0 || err_slot >= sc_ncmds) immediately after reading CCS and before the ap_ccbs[err_slot] deref, routing a bad slot to the existing process_error machinery (which re-reads CCS and the TFES path's check then resets the port).

VERDICT.md verdict full path:line trace + reachability analysis
↓ download raw

DF-1210 β€” VERDICT

Finding: OOB read in the AHCI DHRS error path: a device-controlled CCS slot index is dereferenced before any bounds check (sys/dev/disk/ahci/ahci.c). Status: NOT TESTABLE on this audit guest. Confidence (bug is real): certain. Impact ceiling: kernel OOB array index + garbage-pointer deref β†’ panic / controlled memory access; device-controlled (malicious AHCI controller). Fix: authored in fix.diff, applied clean, compile-validated into the GENERIC kernel (fix_build.log).

Mechanism (confirmed line-by-line in sys/)

  1. ahci_port_intr() (ahci.c) handles the DHRS (D2H Register FIS) interrupt.
  2. ahci.c:2643 β€” int err_slot; declared inside the TFES block; process_error: label is at ahci.c:2651.
  3. DHRS branch ahci.c:2809 (else if (is & AHCI_PREG_IS_DHRS)): - ahci.c:2832-2833 reads tfd (PxCMD_TFD) and cmd (PxCMD). - ahci.c:2843-2844 β€” on (tfd & ERR) && !(cmd & ST): - ahci.c:2845-2846 β€” err_slot = AHCI_PREG_CMD_CCS(ahci_pread(ap, AHCI_PREG_CMD)); AHCI_PREG_CMD_CCS(_r) = ((_r) >> 8) & 0x1f (ahci.h:183) β†’ 5-bit, 0-31. - ahci.c:2847 β€” ccb = &ap->ap_ccbs[err_slot]; NO bounds check. ap_ccbs[] is allocated for sc->sc_ncmds slots (4-32, set from the controller's CAP.NCS). - ahci.c:2850 β€” kprintf("... cmd=%02x", ..., ccb->ccb_xa.fis->command) β€” derefs the OOB ccb then a garbage fis pointer. - ahci.c:2851 β€” goto process_error; carries the (still unchecked) slot.
  4. Compare the TFES path, which does validate first: ahci.c:2724 β€” else if (err_slot < 0 || err_slot >= ap->ap_sc->sc_ncmds) { ... err_slot=-1; }. The DHRS path is the asymmetric, missing-check sibling.

So a CCS value in [sc_ncmds .. 31] indexes past ap_ccbs[]. The damage (read of OOB ccb, then fis->command deref) occurs at ahci.c:2847-2850 before any later check can run.

Why it is NOT TESTABLE on this guest

  • pciconf -l lists no AHCI/SATA controller; the guest root disk is virtio-blk (/dev/vbd0), the only storage besides a SCSI DVD-ROM. ahci(4) is in GENERIC but did not attach to any device, so ahci_port_intr() is dead code at runtime here.
  • The attacker-controlled input (err_slot via CCS) is a hardware register read from the AHCI controller. On a benign controller CCS is always a slot the driver actually issued (< sc_ncmds). There is no userspace syscall that can make a benign QEMU AHCI model return CCS >= sc_ncmds, so even if an AHCI device were added it would not fire from an unprivileged user.

Valid "device-controlled, not reachable from an unprivileged user on this guest" case. The bug is genuine (asymmetric missing bounds check confirmed against the sibling TFES path); threat model = malicious/buggy AHCI controller (peripheral / malicious VM device-model).

Exploit chain

None developed: the primitive is reachable only via a malicious AHCI controller's CCS register, not via any unprivileged local syscall. On this guest there is additionally no AHCI device at all. Documented impact ceiling: panic / OOB memory access driven by the controller.

Fix

fix.diff adds the same bounds check the TFES path uses, immediately after the CCS read and before ap_ccbs[err_slot]:

err_slot = AHCI_PREG_CMD_CCS(ahci_pread(ap, AHCI_PREG_CMD));
if (err_slot < 0 || err_slot >= ap->ap_sc->sc_ncmds) {
    kprintf("%s: DHRS bad error slot %d\n", PORTNAME(ap), err_slot);
    err_slot = -1;
    goto process_error;
}
ccb = &ap->ap_ccbs[err_slot];

A bad slot is routed through process_error (which re-reads CCS and falls into the TFES err_slot < 0 β†’ failall/reset handling). Minimal, targeted, symmetric with the existing TFES guard.

Build / run on this guest

./build.sh && ./run.sh is a reachability probe. On this guest it reports "no ahci controller attached β†’ DHRS path unreachable"; the bug is confirmed by the source trace above and the fix is compile-validated into GENERIC.

Fix verification

not_testable

compile validated -Werror

module/kernel build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. ahci DHRS err_slot=CCS no bounds check vs ap_ccbs[sc_ncmds]. No AHCI controller.