# DF-1018 — dastart TRIM unbounded ranges heap overflow + OOB DMA — VERDICT

## Verdict: INCONCLUSIVE (runtime) / CONFIRMED (source-level)

The bug is **confirmed real** by line-by-line source tracing. Runtime
reproduction is blocked by missing hardware (no `da` device on this guest).

## Mechanism (source-level trace)

1. **Attacker input:** A malicious SCSI device returns READ_CAPACITY with
   `length = 1` (or any small value), setting `softc->params.secsize = 1`
   via `dasetgeom()` (no validation — see DF-1017).

2. **DAIOCTRIM ioctl** at `scsi_da.c:463`:
   `byte_count = MIN(bytes_left, 0x7FFF8000);` — caps b_bcount at ~2GB.
   Then `bp->b_bcount = byte_count` and `dev_dstrategy()` enqueues the bio.

3. **dastart TRIM path** at `scsi_da.c:1341`:
   `count = bp->b_bcount / softc->params.secsize;` — with secsize=1,
   `count = 0x7FFF8000 = 2147418112`.

4. **Unbounded inner loop** at `scsi_da.c:1345-1360`:
   ```c
   while (count > 0) {
       int c = min(count, 0xffff);
       int off = ranges * 8;
       req->data[off+0..7] = ...;   // writes into fixed-size data[4096]
       ranges++;                     // NO bound check against TRIM_MAX_RANGES
   }
   ```
   `req->data` is `uint8_t data[TRIM_MAX_RANGES * 8]` = `data[512*8]` = 4096 bytes
   (scsi_da.c:135, TRIM_MAX_BLOCKS=8, TRIM_MAX_RANGES=512).
   With count=2.1B, the loop produces ~32768 ranges, writing 262144 bytes into
   a 4096-byte array → **258KB heap overflow** past `trim_request.data[]`
   into `bios[]` and `da_softc`.

5. **Merge check gap:** The merge check at `scsi_da.c:1365-1367` only bounds
   SUBSEQUENT bios (`bio1 = bioq_first(...)`), not the FIRST bio which is
   processed unconditionally in the `while(1)` loop.

6. **OOB DMA (info leak):** `cam_fill_csio()` at `scsi_da.c:1378` sets
   `dxfer_len = ((ranges+63)/64)*512` = ~262KB with `data_ptr = req->data`
   (only 4096 valid). The SCSI controller DMAs ~258KB of kernel heap
   content to the device — **kernel memory disclosure to a malicious device**.

## Exploit chain

The overflow writes ~258KB past `trim_request.data[]`, corrupting:
- `trim_request.bios[]` (512 pointers at offset 4096)
- Subsequent `da_softc` fields
- Adjacent kernel heap objects

With heap grooming, this is a **kernel heap write primitive**. However,
the trigger requires a malicious SCSI device with `secsize=1`, which is a
device-attacker scenario (not local user). The trigger is also reachable
via root DAIOCTRIM with a malicious device. This is a **device-attacker**
exploit, not a local privilege escalation from an unprivileged user.

## Why runtime reproduction is blocked

No `da` device on this QEMU guest (only DVD-ROM). The TRIM code path in
`scsi_da.c` requires a Direct Access SCSI disk.

## PoC changes

Created `harness.c` — arithmetic demonstration showing 32768 ranges into
data[512] = 258048 byte overflow, plus OOB DMA dxfer_len calculation.
Built and verified on the guest.

## Fix validation

The fix (`fix.diff`) adds `if (ranges >= softc->trim_max_ranges) break;`
at the top of the inner `while(count>0)` loop. Applied + compiled in the
combined-fix kernel (`#1`, rc=0, boots cleanly). Runtime before/after
not possible (no da device).

**fix_status: not_testable** (diff applies + compiles; runtime blocked by
missing HW).

## Recommended fix

Bound `ranges` against `softc->trim_max_ranges` inside the inner loop:
```c
if (ranges >= softc->trim_max_ranges)
    break;
```
Also clamp `dxfer_len` at `cam_fill_csio` to `sizeof(req->data)`.
Matches the finding proposal. The inner-loop bound is the root-cause fix.
