idad_dump reports fake success for controller-failed/rejected dump writes β ida_command discards the controller error status (DF-5027 pattern)
Summary
ida_done detects HARD_ERROR/CMD_REJECTED then throws the status away for IDA_COMMAND (the driver's own XXX at ida.c:358) - a controller-rejected crash-dump write reports success and savecore believes a corrupt/short dump. Per-qcb error + propagate fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5047 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.6 KB | β raw | |
| VERDICT.md | β | 3.5 KB | β raw | |
| env.txt | β | 1.0 KB | view raw | |
| fix.diff | β | 896 B | view raw | |
| verdict.json | β | 3.4 KB | view raw | |
| manifest.json | β | 1.0 KB | view raw |
DF-5047 β idad_dump reports fake success for controller-rejected/failed dump writes (DF-5027 pattern in IDA)
File: sys/dev/raid/ida/ida_disk.c (pass 2, GLM 5.3)
Bucket: base:dev | Severity: Low | Pattern: DF-5027 (ips_disk fake-success dumps)
What the bug is
idad_dump() (sys/dev/raid/ida/ida_disk.c:191-211) hands each dump chunk to
ida_command() and returns its error straight to the crash-dump machinery:
if (ap->a_length > 0) {
error = ida_command(drv->controller, CMD_WRITE, ap->a_virtual,
ap->a_length, drv->drive, ap->a_offset / DEV_BSIZE, DMA_DATA_OUT);
}
But ida_command() (sys/dev/raid/ida/ida.c:317-362) can only ever return
EAGAIN (no QCB, ida.c:329-332) or ETIMEDOUT (5s wait expiry, ida.c:449-461).
The controller's own status for the command is detected and then discarded:
ida_done()readsqcb->hwqcb->req.errorand flags HARD_ERROR / CMD_REJECTED (ida.c:515-524), but forIDA_COMMANDrequests it merely doeswakeup(qcb)(ida.c:526-528) β the error goes nowhere.ida_wait()returns 0 unconditionally once the qcb completes (ida.c:463-467 polling path; :449-453 interrupt path).- The author knew:
/* XXX should have status returned here? */(ida.c:358-359).
Net effect: a dump write that the controller rejected or hard-errored returns
success to dumpsys, which keeps advancing dumplo and finally prints
"Dump complete" (sys/platform/pc64/x86_64/dump_machdep.c:353-364). The saved
crash dump silently lacks or mis-contains the affected pages β exactly the
DF-5027 copperhead pattern, here on Compaq SMART/IDA.
Threat model / trigger
Requires (a) an IDA controller as dump device, (b) a kernel panic, and (c) a controller that fails/rejects the write (failing cache, cable pulled, array gone). Result: silently corrupt/empty core dump during incident response β an integrity failure of the forensic record, not memory unsafety.
PoC / verification status
Hardware-gated (no Compaq SMART/IDA PCI device on the audit guest β pciconf evidence in env.txt; the driver never attaches, no /dev/idad* dump device can be configured). Code trace is line-accurate; see VERDICT.md.
Fix
Record the controller status per-qcb in ida_done() and propagate it out of
ida_command(). See fix.diff (ida.c + idavar.h).
Reproduce (on hardware)
# IDA box: configure dumpdev=/dev/idad0s0?, panic the box while the # controller's write path is failing (pull battery/cache, fail the array): # sysctl debug.debugger=1 ... call panic from ddb # expect: dumpsys prints "ok"/"Dump complete" even though writes failed; # savecore later yields a corrupt/short dump.
DF-5047 VERDICT β idad_dump fake success via discarded controller status
status: untested (hardware-gated) | impact: none demonstrated (crash-dump integrity) | confidence: likely
Reproduced?
No. The trigger needs a physical Compaq SMART/IDA controller configured as
the dump device. Guest evidence (env.txt): pciconf -l shows no vendor
0x0e11 function, so ida_pci_probe never matches (ida_pci.c:193-210) and no
idad device exists (ls /dev/idad* -> NO_IDAD_DEVS). Untested = hardware
absent, not refuted.
Why the finding is real (trace)
- Dump chunk sizes are safe here:
si_iosize_max = min(MAXPHYS, 64*1024)(ida_disk.c:281) ->di.maxiosize(sys/kern/subr_disk.c:934) ->cb_dumpdataclamps chunks tomin(MAXPHYS, maxiosize)= 64KB (sys/platform/pc64/x86_64/dump_machdep.c:168-177), sohwqcb->req.bcount = howmany(len, DEV_BSIZE) = 128fits the u16 field (ida.c:411) and bus_dmamap_load stays within the MAXBSIZE tag (ida.c:209-213). The math is fine; the status reporting is the bug. idad_dumpclears IDA_INTERRUPTS and polls (ida_disk.c:202-208) ->ida_command->ida_waitpolling branch (ida.c:455-467): on completion it callsida_done(ida, qcb)and returns 0. It never checks anything afterwards.ida_donecomputeserrorfromreq.error & HARD_ERROR|CMD_REJECTED(ida.c:515-524) and, forIDA_COMMAND(which dump writes are β flagsDMA_DATA_OUT | IDA_COMMAND, ida.c:350), onlywakeup(qcb)(ida.c:526-528). The error bit dies there.ida_commandreturnserror= whateverida_waitsaid = 0 (ida.c:355-361), with the author's own admission at :358-359 (/* XXX should have status returned here? */).idad_dumpreturns that 0 todev_ddump(sys/kern/kern_device.c:423-446) ->cb_dumpdatatreats it as success and advances (dump_machdep.c:188-192); the run ends with "Dump complete" (:363-364).
A CMD_WRITE rejected by the controller therefore produces a "successful" dump with missing/garbage pages β the same integrity failure class as DF-5027 (ips). Because the affected writes are crash-dump pages only (the normal data path goes through qcb->bio and sets B_ERROR correctly at ida.c:530-532), disk data written by user I/O is unaffected.
Why Low
- Hardware-gated (1999-era Compaq arrays; QEMU cannot emulate one).
- Only exercised during a panic with a failing controller.
- Impact is loss of forensic evidence (silent bad dump), not memory corruption or privilege.
Fix assessment
fix.diff records qcb->error = EIO in ida_done before the command wakeup
and propagates it from ida_command after ida_wait succeeds. For the dump
path (polling, crit-held, single-CPU post-panic) this is race-free and
complete. For the general interrupt-path there remains a narrow
qcb-reuse race (qcb goes back on the free list at ida.c:536 before the
waiter reads qcb->error) β noted in the diff comment; a full fix would hold
the qcb until the status is consumed. fix_status=not_testable: no IDA
hardware on the guest to build and validate a patched kernel.
Bottom line
Real DF-5027-family defect, code-certain (the driver itself marks the missing status return with XXX), untestable here solely for lack of the hardware.
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 (ida.c + idavar.h) stores qcb->error in ida_done before the command wakeup and returns it from ida_command when ida_wait succeeded β dump writes can now report EIO. Race-free on the polling path dumps use (crit-held, single CPU post-panic); a narrow qcb-reuse race remains on the general interrupt path (noted in-diff; full fix would hold the qcb until status is read). No hardware to build/boot a validated kernel.
fix.diff (git apply --check: APPLIES_CLEAN); hunks at ida.c ida_command post-wait and ida_done pre-wakeup, idavar.h struct ida_qcb
Confirmed kernel references
Detail
Evidence (decisive lines)
env.txt (no 0x0e11 PCI function, no /dev/idad* dump device configurable); VERDICT.md step-by-step trace incl. dump_machdep.c chunking proof that sizes are safe; fix.diff (git apply --check clean)
PoC changes
No runnable PoC exists (hardware-gated: needs an IDA controller as dumpdev plus a failing write path). Pack carries the code trace + hardware-absence evidence; fix.diff validated with git apply --check.
Verified recommended fix
Record the controller status per-qcb in ida_done (qcb->error = EIO on HARD_ERROR/CMD_REJECTED) and propagate it out of ida_command after a successful wait.
Verdict
DF-5027-family fake-success dump: idad_dump (ida_disk.c:204-207) propagates ida_command's return, but ida_command can only return EAGAIN/ETIMEDOUT β the controller's HARD_ERROR/CMD_REJECTED status is detected in ida_done (ida.c:515-524) and then discarded for IDA_COMMAND requests (ida.c:526-528 only wakeup(qcb); ida_wait returns 0 at ida.c:466-467; the driver's own 'XXX should have status returned here?' at ida.c:358-359). A controller-rejected crash-dump write therefore reports success and dumpsys prints 'Dump complete' with missing/garbage pages. Untested solely because the guest has no IDA hardware; the dump chunk math itself is safe (si_iosize_max 64KB at ida_disk.c:281 clamps cb_dumpdata chunks, so req.bcount u16 never wraps and dmamap_load stays within the MAXBSIZE tag). Low: panic-time, hardware-failure-gated integrity loss of the crash dump, not memory unsafety; normal user I/O error path (ida.c:530-532) sets B_ERROR correctly.
No comments yet.