# DF-4837 — ida bio path leaves qcb->flags == 0: bounced READ data never copied back + bounce pages leak

- File: `sys/dev/raid/ida/ida.c:391` (with :399-401 and :506-513)
- Severity: Medium (silent disk-data corruption + per-I/O bounce-page leak on
  >4 GB machines with this 32-bit PCI card; benign on ≤4 GB boxes)
- Reproduced: **logic/bounce-accounting harness on the DragonFly guest** (no
  Compaq SMART controller exists in the guest).

## Root cause

`ida_construct_qcb()` clears `qcb->flags = 0` (ida.c:391) and never sets the
DMA direction for disk I/O, so:

- ida.c:399-401 computes the pre-sync as `BUS_DMASYNC_PREWRITE` for **every**
  bio (reads included);
- `ida_done()` gates the post-completion work on
  `qcb->flags & DMA_DATA_TRANSFER` (ida.c:506) — which is 0 for every bio — so
  **`BUS_DMASYNC_POSTREAD` never runs and `bus_dmamap_unload()` is never
  called** for disk I/O.

On machines with >4 GB RAM this is not cosmetic: the buffer tag inherits the
parent's 32-bit limit (ida_pci.c:260-265; DFly clamps child lowaddr,
busdma_machdep.c:272 `MIN(parent->lowaddr, ...)`), so buffer pages above 4 GB
**bounce**. DFly busdma then requires

- `bus_dmamap_sync(PREWRITE)` orig→bounce (busdma_machdep.c:1044-1053) — done,
- `bus_dmamap_sync(POSTREAD)` bounce→orig (busdma_machdep.c:1054-1063) —
  **skipped**: the device's read data stays in the bounce page and the caller's
  buffer keeps stale contents (silent data corruption on every bounced READ),
- `bus_dmamap_unload` to return bounce pages to the zone free list
  (map->bpages, add_bounce_page/return_bounce_pages) — **skipped**: each load
  appends bounce pages that are never returned → unbounded kernel memory leak
  that grows with every disk I/O.

## Build / run (on the DragonFly guest)

```
cc -O2 -Wall -o df4837 df4837_harness.c
./df4837
```

## Expected

- BASELINE single READ: `caller buffer STALE (device data LOST)`,
  `leaked=1`.
- BASELINE 64 READs: `leaked=64` (one bounce page per I/O, none returned).
- FIXED (flags set per direction, per fix.diff): buffer `has device data`,
  `leaked=0` for both arms (page returned by the unload).

See `run.log`, `VERDICT.md`, `fix.diff`.
