# DF-1737 — ata-lowlevel.c heap OOB write via unchecked ATAPI byte-count

## Verdict
**REPRODUCED (logic/harness)** — bug confirmed by source trace. Not
live-triggerable on the default QEMU guest: the QEMU DVD-ROM does not
report bogus byte counts; the realistic trigger is a malicious ATAPI
device (USB-C/SATA bridge, malicious SSD firmware, crafted QEMU ATAPI
emulation).

## Mechanism (path:line)
* `sys/dev/disk/nata/ata-lowlevel.c:329` — `length = ATA_IDX_INB(ch, ATA_CYL_LSB)|(ATA_IDX_INB(ch, ATA_CYL_MSB)<<8);`
  — 0..65535 read directly from device registers with no bounds check.
* `sys/dev/disk/nata/ata-lowlevel.c:361-362` / `:378-379` / `:392-393` /
  `:395-397` — `ata_pio_write/read(request, length); request->donecount += length;`
  — `donecount` advances by the device-reported length.
* `sys/dev/disk/nata/ata-lowlevel.c:365-366` / `:382-383` —
  `request->transfersize = min((bytecount - donecount), transfersize);`
  — unsigned 32-bit subtraction wraps to ~4 GB once `donecount > bytecount`.
* Next IRQ, `ata_pio_read/write` does `ATA_IDX_INSW_STRM(..., (void*)((uintptr_t)request->data + request->donecount), size/2)`
  with `donecount` past end → OOB write up to `transfersize` (64 KB).

## Phase 6 escalation
A live escalation chain on the default guest is **not possible**: the
QEMU ATAPI device is well-behaved. With a malicious ATAPI device, the
primitive is a controlled-size heap write at a controlled offset (the
device controls both the bogus byte count and the data payload it
delivers). On a guest with no SMAP/SMEP/KASLR this is full
attacker-shaped heap corruption → victim-object overwrite → RIP control
→ uid0. Not developed because the precondition (malicious ATAPI device)
is absent.

## PoC
`harness.c` simulates two IRQs from a malicious device reporting
lengths `[4096, 8192]` against a 4096-byte buffer. After the second
IRQ, `donecount = 12288`, which is 8192 bytes past the buffer end; the
unsigned wrap on the next `transfersize` computation is also shown.

## PoC changes
Wrote `harness.c`, `build.sh`, `run.sh`, `VERDICT.md`, `manifest.json`,
`fix.diff`. Original folder was empty.

## Fix
`fix.diff` clamps `length` to `request->bytecount - request->donecount`
right after the device read, before the `switch` on `ATA_IREASON`.
Validated by a clean `nativekernel` rebuild (nata is in GENERIC).
