# DF-1716 — nata ata-pci.c OOB interrupt[] write/read

## Verdict
**REPRODUCED (logic/harness)** — bug confirmed by source trace and a
userspace harness that simulates the exact `interrupt[8]` layout and the
attach/IRQ loops. The bug is *real* but **not live-triggerable on the
default QEMU guest**: the guest has no AHCI controller (NATA binds to the
PIIX3 IDE controller with 2 channels) and reaching `channels > 8` requires
a malicious / hotplug AHCI controller reporting CAP.NP > 7 or PI bits
8..31 set. On such a controller the bug yields up to 384 bytes of heap
OOB write on attach and OOB function-pointer read+indirect-call on every
IRQ; with no SMAP/SMEP/KASLR + heap grooming this is RIP control.

## Mechanism (path:line)
* `sys/dev/disk/nata/chipsets/ata-ahci.c:101-103` —
  `ctlr->channels = MAX(flsl(ATA_AHCI_PI), (ATA_AHCI_CAP & ATA_AHCI_NPMASK)+1)`
  sets channels up to 32 from hardware registers.
* `sys/dev/disk/nata/ata-pci.h:64-67` — `struct { void (*function)(void*); void *argument; } interrupt[8];`
  is hard-coded to 8 slots.
* `sys/dev/disk/nata/ata-pci.c:230` — attach loop adds `ctlr->channels`
  children (up to 32).
* `sys/dev/disk/nata/ata-pci.c:383-384` — `ata_pci_setup_intr` writes
  `controller->interrupt[unit].function/.argument` with no bounds check
  on `unit` against 8.
* `sys/dev/disk/nata/ata-pci.c:584-586` — `ata_generic_intr` iterates
  `unit < ctlr->channels`, reads `interrupt[unit].argument`, then calls
  `interrupt[unit].function(ch)` — OOB read + indirect call on every IRQ.

For `channels = 32`, units 8..31 each touch 16 bytes of OOB heap
(24 × 16 = 384 bytes).

## Phase 6 escalation
A live escalation chain on the default guest is **not possible**: there is
no AHCI device on the QEMU PIIX3-only config, and NATA binds to the legacy
IDE controller with 2 channels. The realistic trigger is a malicious /
hotplug PCIe AHCI device (Thunderbolt, crafted QEMU command line). This
is a valid external-device threat model, not an unprivileged-user
privesc. With such a device the OOB function-pointer read in
`ata_generic_intr` is direct RIP control (no SMEP, user pages executable),
so the chain would be: spray attacker-shaped heap content where the
controller's OOB interrupt[] region is read, place a userspace
`commit_creds(prepare_kernel_cred(0))` trampoline at the address the OOB
`function` field reads, wait for the next IRQ. We did not develop that
chain because it requires the malicious-device precondition the default
guest cannot supply.

## PoC
`harness.c` simulates the controller struct, attach loop, and IRQ loop
with `channels = 32`. Reports 24 OOB writes (Phase 1) and 24 OOB
function-pointer reads+indirect-calls per IRQ (Phase 2).

```
=== DF-1716 nata AHCI OOB interrupt[] harness ===
ctlr->channels = 32 (from CAP.NP+1 / PI), interrupt[] size = 8 slots
Phase 1 result: 24 OOB writes performed (expected 24 for channels=32)
Phase 2 result: 24 OOB-read+call per IRQ (expected 24 for channels=32)
VERDICT: BUG CONFIRMED. AHCI reporting 32 channels causes 24 OOB writes on
attach and 24 OOB function-pointer reads+calls per IRQ (384 bytes heap
corruption, full RIP control with heap grooming).
```

Build: `cc -O2 -Wall -Wextra -o harness harness.c` · Run: `./harness`

## PoC changes
Original folder had no source. Wrote `harness.c`, `build.sh`, `run.sh`,
this `VERDICT.md`, `manifest.json`, and `fix.diff`.

## Fix
`fix.diff` clamps `ctlr->channels` to 8 in `ata_pci_attach` after the
chip-init returns, before the child-attach loop runs. This is the
smallest change that bounds both the attach writes and the IRQ reads
without reworking the fixed-size `interrupt[]` array. Validated by a
clean `nativekernel` rebuild (nata is in GENERIC).
