ips: attach-time info-command timeout unwind frees the DMA buffer and recycles the static command slot while the adapter still owns the command - device UAF write / zeroed-callback call on late completion
Summary
The adapter/drive info commands issue then tsleep a fixed 30s/10s; on ETIMEDOUT they unconditionally unload/free/destroy the info buffer and recycle the slot while the adapter still holds the command: a late completion DMA-writes into the FREED heap and runs the slot's callback which a subsequent get_free_cmd has bzero'd - NULL indirect call panic window or false completion feeding stale data into adapter_info/nvram. Folded in: the other static commands use infinite waits (a wedged card hangs boot forever). Distinct from DF-4614 (teardown frees under live commands). Don't-reclaim-on-timeout + finite-budgets fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4640 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | β | 4.3 KB | β raw | |
| timeout_uaf_path.c | β | 2.1 KB | view raw | |
| env.txt | β | 208 B | view raw | |
| verdict.json | β | 4.2 KB | view raw | |
| manifest.json | β | 1.1 KB | view raw |
DF-4640 β ips attach-time info commands: timeout unwind frees the static command slot and its DMA resources while the adapter still owns the command
File: sys/dev/raid/ips/ips_commands.c:313-332 (adapter info, 30 s budget), :411-433 (drive info, 10 s)
Severity: Low (trigger = adapter that exceeds the timeout, i.e. failing/flaky hardware) Β· Confidence: certain (code path unconditional once the timeout fires)
Class: CWE-416 (use-after-free of DMA backing) + NULL/wrong indirect callback on late completion
No runnable PoC exists for this class on the audit guest: the path requires a
real IBM ServeRAID PCI adapter that is slow (not dead) β QEMU has no device
model, the guest has zero 0x1014/0x9005 devices and no /dev/ips* (env.txt).
The evidence is the line-accurate static trace below; timeout_uaf_path.c
encodes it as an annotated path listing.
Trace
ips_adapter_initβips_get_adapter_info(ips.c:426, ips.c:336) βips_send_adapter_info_cmd(ips_commands.c:288) with the STATIC command slot 0 (IPS_STATIC_FLAG, ips.c:211-217).- The info buffer is allocated (
bus_dmamem_alloc, :307), the load callback issues the command to the adapter (sc->ips_issue_cmd, :284 viaips_adapter_info_callback). ips_timed_wait(command, "ips", 30*hz)(:317) β the ONLY completion signal is the adapter's interrupt (ips_wakeup_callback, :66-72). If the adapter is wedged-but-alive, tsleep returns ETIMEDOUT.- The unwind then does, unconditionally:
-
bus_dmamap_unload(:325) β unload while the device may still DMA, -bus_dmamem_free(data_dmatag, data_buffer, ...)(:328) β the DMA target buffer is returned to the kernel heap while the adapter still holds the command, -bus_dma_tag_destroy(data_dmatag)(:330), -ips_insert_free_cmd(:331) β clearsIPS_STATIC_BUSYso slot 0 can be handed out again (ips_get_free_cmd, ips.c:211-217) β during attach, the very next consumer isips_update_nvram(ips.c:432) orips_get_drive_info(ips.c:437). - When the adapter eventually completes the stale command:
- hardware DMA-writes IPS_ADAPTER_INFO_LEN bytes into the freed
buffer (device-side UAF write into kernel heap), and
-
ips_morpheus_check_intr(ips.c:567-576) /ips_copperhead_intrexecutecommandarray[0]->callback(command): if slot 0 was re-gotten,ips_get_free_cmdhas zeroedcallback(ips.c:232 bzero region includescallback) β NULL indirect call, kernel panic; if the new owner already installed its callback, the NEW command is falsely completed (ips_timed_waitreturns while its data buffer is still in flight βmemcpy(&sc->adapter_info, ...):322 of stale data / nvram write of a half-DMA'd page :603-608).
Distinction from the filed ips family
- DF-4614 covers the queue teardown frees (
ips_cmdqueue_free/ips_cmdqueue_initat ips.c) freeing under live commands on detach/error. DF-4640 is the per-command timeout unwind inside ips_commands.c handing back a slot and freeing DMA resources the hardware still owns β a different call site and a different trigger (no detach needed). - DF-4613 covers the watchdog-path lock inversion; the sibling wait-family
defect noted here is that every OTHER static command uses
ips_timed_wait(..., 0)= infinite tsleep (ips_commands.c:471, :558, :673, :722, :743), so a card wedging mid-attach hangs the boot thread forever rather than timing out.
Fix sketch (see verdict.json recommended_fix)
On ETIMEDOUT, do NOT free/recycle: leave the command owned (keep IPS_STATIC_BUSY set, keep the DMA allocation) and let the watchdog (ips_timeout) β or attach failure path with the adapter quiesced β reclaim it; alternatively fail attach hard (ENXIO) without recycling slot 0 until the adapter is reset, and give the infinite waits finite budgets.
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.
(No fix.diff for this item: documented recommendation only - see Fix above.)
Fix verification
not_testableNo A/B possible: the trigger needs real ServeRAID hardware that stalls >30 s at attach; the guest has none (env.txt) and QEMU has no ips device model.
env.txt (0 ServeRAID PCI devices, /dev/ips* absent)
Confirmed kernel references
- sys/dev/raid/ips/ips_commands.c:313
- sys/dev/raid/ips/ips_commands.c:317
- sys/dev/raid/ips/ips_commands.c:325
- sys/dev/raid/ips/ips_commands.c:328
- sys/dev/raid/ips/ips_commands.c:331
- sys/dev/raid/ips/ips_commands.c:415
- sys/dev/raid/ips/ips_commands.c:426
- sys/dev/raid/ips/ips_commands.c:432
- sys/dev/raid/ips/ips.c:232
- sys/dev/raid/ips/ips.c:567
- sys/dev/raid/ips/ips.c:573
Detail
Exploit chain
requires hostile/flaky hardware: attach -> adapter exceeds 30 s -> freed buffer + recycled slot -> late completion -> device DMA write into freed heap / NULL callback call / false completion of the slot's new command
Evidence (decisive lines)
VERDICT.md (5-step trace with path:line), timeout_uaf_path.c (annotated path), env.txt (no ServeRAID hardware on guest)
PoC changes
n/a - no PoC possible without ServeRAID hardware (same constraint as DF-4613/4615); pack documents the static trace and the hardware absence
Verified recommended fix
On ETIMEDOUT in ips_send_adapter_info_cmd/ips_send_drive_info_cmd, do not unload/free/recycle: keep IPS_STATIC_BUSY set and the DMA allocation until the adapter is reset or attach aborts with the card quiesced; give the infinite ips_timed_wait(...,0) waits finite budgets.
Verdict
The timeout unwinds of the attach-time info commands free the hardware-owned command's resources: ips_send_adapter_info_cmd (ips_commands.c:317-331) and ips_send_drive_info_cmd (:414-432) tsleep for a fixed 30 s / 10 s; on ETIMEDOUT they bus_dmamap_unload + bus_dmamem_free + bus_dma_tag_destroy the info buffer and clear IPS_STATIC_BUSY (ips_insert_free_cmd, :331/:432) while the adapter still holds the command. A late adapter completion then (a) DMA-writes IPS_ADAPTER_INFO_LEN / IPS_DRIVE_INFO_LEN bytes into the freed kernel heap allocation (device-side UAF write) and (b) runs commandarray[0]->callback via ips_morpheus_check_intr (ips.c:567-576) / ips_copperhead_intr on a slot that ips_get_free_cmd (ips.c:232) has zeroed - NULL indirect call panic window - or falsely completes the slot's new owner (early ips_timed_wait return, memcpy of stale data into sc->adapter_info at :322). Trigger is a slow-but-alive ServeRAID adapter at attach, i.e. failing hardware, hence Low severity; the code path itself is unconditional once the timeout fires. Not reproduced: the audit guest has no ServeRAID PCI device and QEMU has no ips device model (env.txt), so the path cannot be instantiated; the line-accurate trace in VERDICT.md/timeout_uaf_path.c is the evidence. Distinct from DF-4614 (ips_cmdqueue_free teardown frees under live commands at ips.c:116-143) - this is the per-command timeout unwind at ips_commands.c:325-331/426-432. Sibling wait-family defect folded in: all other static commands use ips_timed_wait(...,0) = infinite tsleep (ips_commands.c:471,558,673,722,743), so a card wedging mid-attach hangs boot forever instead.
No comments yet.