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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4640 Β· 5 files
FileTypeDescriptionSize
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
VERDICT.md
↓ download 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

  1. 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).
  2. The info buffer is allocated (bus_dmamem_alloc, :307), the load callback issues the command to the adapter (sc->ips_issue_cmd, :284 via ips_adapter_info_callback).
  3. 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.
  4. 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) β€” clears IPS_STATIC_BUSY so slot 0 can be handed out again (ips_get_free_cmd, ips.c:211-217) β€” during attach, the very next consumer is ips_update_nvram (ips.c:432) or ips_get_drive_info (ips.c:437).
  5. 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_intr execute commandarray[0]->callback(command): if slot 0 was re-gotten, ips_get_free_cmd has zeroed callback (ips.c:232 bzero region includes callback) β†’ NULL indirect call, kernel panic; if the new owner already installed its callback, the NEW command is falsely completed (ips_timed_wait returns 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_init at 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.

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_testable
baseline no→ patch + rebuild →patched clean

No 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)
per-fix-DF-4640

Confirmed kernel references

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.