OOB array access in FBSS saved-commands reissue loop (hardcoded 32 vs sc_ncmds)
Summary
ahci_issue_saved_commands() FBSS branch at ahci.c:3967-3987: loop for(i=0;i<32;++i) hardcoded 32, not sc_ncmds(4-32). ci_saved from hardware CI/SACT at ahci.c:3890-3891 via timeout path with NO KKASSERT validation (unlike main intr path at :2622). ccb_at=ap->ap_ccbs[i].ccb_xa.at OOB if i>=sc_ncmds. Then ccb_at->at_target derefs garbage ptr. Requires AP_F_FBSS_ENABLED (CAP.FBSS or force_fbss tunable). Malicious controller stalls cmd until timeout then returns CI/SACT with bits beyond sc_ncmds. Fix: mask ci_saved to valid slot range before loop.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1212 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | full narrative + path:line trace | 4.0 KB | β raw |
| README.md | readme | reproduce / preconditions | 1.4 KB | β raw |
| fix.diff | suggested-fix | mask ci_saved to (1<<sc_ncmds)-1 in FBSS branch | 434 B | view raw |
| build.sh | build-log | nativekernel build validation | 379 B | view raw |
| run.sh | run-log | no runtime trigger; static trace | 450 B | view raw |
| env.txt | environment | guest uname, PCI devices, GENERIC config | 587 B | view raw |
| build.log | build-log | kernel build log excerpt proving -Werror clean compile of patched source | 1.1 KB | 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 |
DF-1212 β ahci FBSS saved-commands OOB array access
Reproduce
Not runnable on the audit guest β no AHCI controller is attached (only PIIX4
IDE + virtio-blk). The verification is a static source trace + a compiling
fix.diff; see VERDICT.md for the line-by-line trace.
To validate the fix compiles, after applying fix.diff to /usr/src:
cd /usr/src
make -j6 nativekernel KERNCONF=X86_64_GENERIC # ~6-8 min from warm obj
The kernel must link cleanly (=== NK_DONE rc=0 ===).
Bug location
sys/dev/disk/ahci/ahci.c:3972 β hardcoded loop for (i = 0; i < 32; ++i)
in ahci_issue_saved_commands() FBSS branch, while ap->ap_ccbs is sized
only sc->sc_ncmds (4..32). Out-of-range ci_saved bits from hardware
CI/SACT registers cause ap->ap_ccbs[i].ccb_xa.at to be read OOB.
Trigger preconditions (NOT met on this guest)
- An AHCI controller that supports FIS-Based Switching Saved (FBSS) β
AP_F_FBSS_ENABLED. - The controller reports
CAP.NCS < 31(so the array is smaller than 32). - A malicious or buggy controller that stalls a command to timeout and then returns CI/SACT bits beyond the reported slot count.
Files
VERDICT.mdβ full narrative withpath:linecitations.fix.diffβ git-apply-able unified diff maskingci_savedto valid slots.env.txtβ guest environment (uname, PCI devices, kernel config).build.logβ relevant excerpt of the single-fix kernel build.
DF-1212 β ahci FBSS saved-commands OOB array access
Verdict
INCONCLUSIVE (not_testable on this guest) β bug confirmed in source; runtime
not triggerable on this guest (no AHCI controller). Fix authored, applied to
in-guest source, and compiled clean as part of a single-fix nativekernel build.
Finding summary
ahci_issue_saved_commands() (sys/dev/disk/ahci/ahci.c) has an FBSS (FIS-Based
Switching Saved) branch whose reissue loop is hardcoded to for (i = 0; i < 32; ++i)
even though ap->ap_ccbs is allocated with only sc->sc_ncmds slots
(sc_ncmds = AHCI_REG_CAP_NCS(cap), 4β32). The hardware-supplied ci_saved
mask comes straight from AHCI_PREG_SACT / AHCI_PREG_CI (read at
ahci.c:3890β3891 on the timeout path with NO KKASSERT validation, unlike the
main interrupt path at :2622). A malicious or buggy controller that stalls a
command until timeout, then returns CI/SACT with bits set beyond sc_ncmds,
causes ap->ap_ccbs[i].ccb_xa.at to be read OOB; the resulting ccb_at is
then dereferenced (ccb_at->at_target) β kernel memory disclosure / panic.
Source confirmation (audited tree)
- ahci.c:370 β
ap->ap_ccbs = kmalloc(sizeof(struct ahci_ccb) * sc->sc_ncmds, β¦)β array sizedsc_ncmds. - ahci.c:3890β3891 β
ci_savedread directly from hardware CI/SACT registers (no bounds validation on this path). - ahci.c:3960 β FBSS branch taken only when
AP_F_FBSS_ENABLEDis set. - ahci.c:3972 β
for (i = 0; i < 32; ++i)β HARDCODED 32. - ahci.c:3975 β
ccb_at = ap->ap_ccbs[i].ccb_xa.at;β OOB read wheni >= sc_ncmds. - ahci.c:3977 β
fis_target = ccb_at->at_target;β derefs the garbage pointer. - ahci_attach.c:349 β
sc->sc_ncmds = AHCI_REG_CAP_NCS(cap);β value 4..32 from hardware. - ahci.h:50 β
AHCI_REG_CAP_NCS(_r)extracts the 5-bit NCS field.
Compare to the main interrupt path at ahci.c:2622, where the analogous slot
extraction is KKASSERT-guarded; the timeout/FBSS path skips that guard.
Why not runtime-reproduced on this guest
The QEMU/KVM guest exposes one PIIX4 IDE controller (atapci, chip=0x70108086)
plus virtio-blk for storage. There is no AHCI controller attached, so the
ahci driver's attach() never runs, no ahci_port/ap_ccbs array is ever
allocated, and the FBSS code path is dead at runtime on this guest. The driver
is statically compiled into the GENERIC kernel (device ahci in
sys/config/X86_64_GENERIC), but it has no live softc.
Triggering the bug requires a real (or emulated) AHCI controller that
(a) reports CAP.NCS < 31, (b) supports / has AP_F_FBSS_ENABLED, and
(c) returns CI/SACT bits beyond the reported slot count on a timeout β
i.e. a malicious or faulty controller. This is a hardware-supplied-input
finding; it cannot be exercised on the audit guest.
Fix (fix.diff)
Mask ci_saved to the valid slot range at the top of the FBSS branch:
} else if (ci_saved != 0) {
ci_saved &= (1U << ap->ap_sc->sc_ncmds) - 1;
...
This is minimal, targeted at the root cause (untrusted hardware-supplied slot bits), and matches the finding's recommended approach. The non-FBSS branch is unaffected (the KKASSERT there guards the same invariant for ordinary ports).
Fix validation
git apply --check -p1against read-onlysys/β clean.- Applied to in-guest
/usr/src, built withmake -j6 nativekernel KERNCONF=X86_64_GENERIC(together with DF-1214 and DF-1223) β compiled clean,NK_DONE rc=0. The fix lineci_saved &= (1U << ap->ap_sc->sc_ncmds) - 1;is present at the built source. fix_status: not_testableβ the bug path is unreachable at runtime on this guest (no AHCI HW), so a before/after PoC run is not possible; the diff is validated to apply + compile and traced to close the code path.
Run / reproduce
Not runnable on this guest β no AHCI hardware. To reproduce on a system with a real AHCI controller that supports FBSS, one would need either a malicious PCIe device or a fault-injection harness that returns CI bits beyond NCS on a timeout. The static trace above is the verification.
Fix verification
not_testablecompile validated
kernel/module build rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. ahci FBSS ci_saved no mask vs sc_ncmds -> OOB ap_ccbs read. ahci in GENERIC, no AHCI HW.
No comments yet.