# DF-4837 — VERDICT

**REPRODUCED (logic/bounce-accounting harness; guest has no Compaq SMART/IDA
controller — DF-4559 methodology).**

## The code path (traced)

1. `ida_construct_qcb()` — sys/dev/raid/ida/ida.c:373-417:
   - :391 `qcb->flags = 0;` — **never** given `DMA_DATA_IN`/`DMA_DATA_OUT`
     for the bio path (contrast `ida_command()` :350, which sets
     `flags | IDA_COMMAND` for internal commands).
   - :396-401 loads the buffer and syncs with
     `op = qcb->flags & DMA_DATA_IN ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE`
     — always PREWRITE, including for `BUF_CMD_READ` bios.
2. `ida_done()` — ida.c:499-538:
   - :506 `if (qcb->flags & DMA_DATA_TRANSFER)` — 0 for every bio → the whole
     block is skipped:
     - :509-511 POSTREAD sync never executes,
     - :512 `bus_dmamap_unload()` never called.
3. Why it matters only above 4 GB: the buffer tag is created with
   `lowaddr = BUS_SPACE_MAXADDR` (ida.c:209-213) but DFly clamps the child to
   the parent's `BUS_SPACE_MAXADDR_32BIT` (ida_pci.c:260-265;
   busdma_machdep.c:272 `newtag->lowaddr = MIN(parent->lowaddr, newtag->lowaddr)`),
   so on >4 GB machines pages above 4 GB bounce (`addr_needs_bounce`,
   busdma_machdep.c:180-189).
4. Bounce data path (busdma_machdep.c:1034-1067): the device DMAs into the
   bounce page; only `bus_dmamap_sync(BUS_DMASYNC_POSTREAD)` copies bounce→orig
   (:1054-1063). Skipped ⇒ every bounced READ completes "successfully" with
   the buffer's previous contents — silent disk data corruption for the FS
   layer. `add_bounce_page` appends to `map->bpages`; only unload/`return_bounce_pages`
   gives them back — skipped ⇒ one leaked bounce page per I/O, unbounded.

On ≤4 GB machines the tag never bounces (`lowaddr ≥ ptoa(Maxmem)` ⇒ no
`BUS_DMA_BOUNCE_LOWADDR`), sync/unload are bookkeeping-only and the bug is
latent — which is why this ancient driver "works" on small boxes.

## What the harness proves (run.log)

Faithful model of the map/zone bookkeeping (`map_load` reserves a bounce page
per load; PREWRITE copies orig→bounce; POSTREAD copies bounce→orig; unload
returns pages) driven by the verbatim flag logic of ida.c:

- BASELINE, 1 READ: buffer stays STALE (device data lost), 1 bounce page held.
- BASELINE, 64 READs: still stale, **64 bounce pages held (none returned)**.
- FIXED (per-direction flags per fix.diff): buffer receives the device data,
  0 pages held after either arm (page returned by unload).

## Impact

- Integrity: silently wrong data returned for filesystem reads whose pages
  live above 4 GB (no error surfaced — bio completes B_OK).
- Availability: unbounded kernel-memory leak growing with every bounced I/O;
  eventual memory pressure / stall.
- Precondition: >4 GB RAM + a Compaq SMART/IDA (32-bit PCI) controller — old
  card in a big box (data-recovery setups are exactly this). No attacker
  control needed beyond ordinary disk I/O once the config exists.

## Fix

Set the DMA direction flags on the bio path (and, in `ida_command()`, assign
`qcb->flags` before the pre-sync instead of after) — `fix.diff`.

## 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.
