Hardware-fed CI/SACT consistency KKASSERT (and CI==0 asserts) panic on chipset misbehavior the driver documents and recovers from elsewhere
Summary
ahci_port_intr() KKASSERTs that the hardware CI/SACT snapshot is a subset of the software slot masks, and ahci_get_err_ccb()/ahci_put_err_ccb() assert/panic on hardware CI != 0. The driver's own comments and recovery code document that chipsets (AMD after IFS errors) leave stale CI/SACT bits after a port stop, and the timeout path even warns-and-continues for it - but the next port_intr (invoked by the timeout path itself after software masks were cleared) trips the assert: recoverable error becomes guaranteed kernel panic. KKASSERT is always active in production DFly kernels. SATA device/chipset error condition during command timeout on affected hardware -> kernel panic (DoS). Same trust model as DF-1210/1211 but panic-only impact.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3772 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.0 KB | β raw | |
| VERDICT.md | β | 2.8 KB | β raw | |
| env.txt | β | 551 B | view raw | |
| fix.diff | β | 1.7 KB | view raw | |
| verdict.json | β | 2.2 KB | view raw |
DF-3772 β KKASSERT on hardware-fed CI/SACT consistency panics on chipset misbehavior the driver itself anticipates
File: sys/dev/disk/ahci/ahci.c (pass 2, GLM 5.3)
What the bug is
ahci_port_intr() opens every invocation with:
ci_saved = ahci_pread(ap, AHCI_PREG_SACT or AHCI_PREG_CI); /* ahci.c:2615/2618 */
KKASSERT(!(ap->ap_sactive && ap->ap_active)); /* ahci.c:2621 */
KKASSERT((ci_saved & (ap->ap_sactive | ap->ap_active)) == ci_saved); /* ahci.c:2622 */
The second assertion requires the hardware CI/SACT register to be a subset of the driver's software masks. DragonFly's own comments document that real chipsets violate exactly this invariant:
- ahci.c:3117-3123: "If we are stopped the AHCI chipset is supposed to have cleared CI and SACT. Did it? If it didn't we try very hard to clear the fields⦠IFS errors on (at least) AMD chipsets create this confusion."
- ahci.c:3933-3948 (timeout path): detects
ahci_pactive() != 0after a stop, prods the chip, and on failure only prints "chipset failed to clear active cmds" β i.e. the driver is designed to survive stale CI/SACT bits. - ahci.h:1117-1122 (port_stop warning): stale CI/SACT races are expected.
But the recovery code at ahci.c:3125-3139 runs after the assert. Concrete panic sequence (no other bug needed):
- A command times out;
ahci_ata_cmd_timeout()(ahci.c:3892) stops the port. - An affected chipset leaves the slot's CI bit set (the documented AMD-IFS behavior).
- Timeout processing clears the software mask (ahci.c:3902/3905), restarts
the port (3930), notices
ahci_pactive() != 0(3940), prods, gives up with a kprintf (3943-3947). - The very next
ahci_port_intr()(invoked by the timeout path itself at ahci.c:3951) re-reads CI with the stale bit set while the software mask no longer has it β KKASSERT at ahci.c:2622 fails β kernel panic.
Related asserts with the same trust model on the same error paths:
- ahci_get_err_ccb(): KKASSERT(ci == 0) (ahci.c:3351) on the hardware CI
register read at 3346 β violated by the same stale-CI condition.
- ahci_put_err_ccb(): unconditional panic("β¦but CI %08x != 0")
(ahci.c:3398-3403).
Impact: a device/chipset error condition that the driver is otherwise able to recover from converts into a guaranteed kernel panic β denial of service. Same attacker model as DF-1210/DF-1211 (device-fed registers), but impact is panic, not OOB memory access β Low.
Why not reproduced on the guest
No AHCI controller on the audit guest (see env.txt). The assert is only
reachable through a real AHCI interrupt/error path. Source-level reasoning is
line-accurate; verdict not_reproduced (device absent).
Reproduce on suitable hardware
Any AHCI chipset known to leave CI/SACT dirty across a port stop during IFS error recovery (driver comments call out AMD 880G/SB850-class parts), plus a command timeout (hung/errored device). Alternatively a QEMU ich9-ahci regression test that deasserts CI improperly.
DF-3772 VERDICT β not_reproduced (no AHCI hardware on guest); panic path certain by source
Bottom line
ahci_port_intr() asserts that the hardware-fed CI/SACT register snapshot is a
subset of the driver's software slot masks (ahci.c:2621-2622). The driver's own
comments and recovery code (ahci.c:3117-3139, 3933-3948) document that real
chipsets β specifically AMD parts after IFS errors β leave stale CI/SACT bits
after a port stop, and the driver is written to survive that condition. The
KKASSERT runs before any of that recovery on every interrupt, so the documented
misbehavior is turned into a guaranteed panic() instead of the intended
recovery. The same pattern exists as KKASSERT(ci == 0) on a hardware read in
ahci_get_err_ccb() (ahci.c:3346-3351) and as an explicit
panic("β¦but CI %08x != 0") in ahci_put_err_ccb() (ahci.c:3397-3403).
Chain (why the assert fires despite the "recovery")
- ahci.c:3890-3907 β timeout: read CI/SACT, stop port, clear software masks for expired slots.
- ahci.c:3930 β restart port.
- ahci.c:3940-3948 β detect stale hardware bits (
ahci_pactive() != 0), prod, then give up with a warning only ("what do we do now?" β ahci.c:3137). - ahci.c:3951 β call
ahci_port_intr(ap, 0); at ahci.c:2615/2618 the stale bits are read intoci_saved; software masks were cleared at 3902/3905; KKASSERT at 2622 fails β panic.
No memory corruption β the subsequent walks are properly masked by software
bits (checked: ahci.c:3080 ci_masked = ci_saved & *active & ~ap->ap_expired,
ahci.c:3152 ci_masked = ~ci_saved & *active). The defect is purely the
assert's contradiction with the driver's own hardware-behavior assumptions.
CWE-617 (reachable assertion) β DoS only β Low.
Not-reproduced rationale
The guest has zero AHCI hardware (env.txt: pciconf empty, virtio-blk root, ahci static in GENERIC). The assert lives on the AHCI interrupt path and cannot be entered. No amount of guest setup short of a different QEMU machine config (vm.sh is fixed to virtio-blk) can instantiate it.
Recommended fix
Replace the hardware-subset KKASSERT at ahci.c:2622 with mask-and-warn (keep
the software-only invariant assert at 2621), downgrade ahci_get_err_ccb()'s
KKASSERT (ahci.c:3351) to a warning + CI register clear, and replace
ahci_put_err_ccb()'s panic() (ahci.c:3398) with a kprintf + register clear.
fix.diff generated from a scratch worktree and verified to apply cleanly
against the pristine tree.
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 authored from line-accurate analysis; no hardware to build/boot-test against.
fix.diff in this pack.
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md step-by-step chain (ahci.c:3890-3951 -> 2622); driver's own stale-CI documentation at ahci.c:3117-3139; related hardware-fed asserts at ahci.c:3351 and panic at ahci.c:3398
PoC changes
No runtime PoC possible - no AHCI controller on the audit guest (virtio-blk only).
Verified recommended fix
Replace the CI/SACT subset KKASSERT with mask-and-warn (chipset-stale bits), downgrade get_err_ccb KKASSERT and put_err_ccb panic to warnings + register clear.
Verdict
ahci_port_intr() KKASSERTs that the hardware CI/SACT snapshot is a subset of the software slot masks (ahci.c:2621-2622), but the driver's own comments and recovery code (ahci.c:3117-3139, ahci.c:3933-3948) document that chipsets (AMD after IFS errors) leave stale CI/SACT bits after a port stop. In the timeout path the software masks are cleared at ahci.c:3902/3905, stale hardware bits are detected at 3940-3948 with only a warning, and the port_intr() call at ahci.c:3951 then trips the assert at 2622 - a recoverable error becomes a guaranteed kernel panic. Same trust model as DF-1210/DF-1211 but panic-only impact. Not reproduced: guest has no AHCI controller (env.txt).
No comments yet.