# DF-5175 — CIS-controlled PCMCIA I/O window size drives out-of-allocation port I/O in `ata_pccard_attach()`

## What

`sys/dev/disk/nata/ata-card.c` wires ATA register access offsets onto the I/O
resource returned by the pccard bus without validating that the resource
actually covers those offsets. On 16-bit PCMCIA, the size of that resource is
the **I/O window length declared in the card's CIS** (CFE tuple), i.e. fully
card-controlled. Two windows of sizes make the wiring go out of bounds:

* **size 9..14** — `rman_get_size(io) > ATA_IOSIZE` (ata-card.c:103) is true,
  so `ATA_CONTROL` is mapped at fixed offset **14** into the window
  (ata-card.c:104-106). A 1-byte access at `window_start+14` lies outside the
  allocation for every size ≤ 14.
* **size 1..7** — the `> ATA_IOSIZE` test is false, the driver takes the
  separate-ctlio branch, and the primary registers are wired at offsets
  0..7 (ata-card.c:93-96) against a window shorter than 8 ports.

Every subsequent ATA control-path access then performs out-of-allocation I/O:

* writes: `ATA_IDX_OUTB(ch, ATA_CONTROL, …)` from `ata_generic_reset()` /
  command setup (`sys/dev/disk/nata/ata-lowlevel.c`) — 1-byte writes of the
  ATA control byte (values from the small set 0x00/0x02/0x04/0x08/0x0a/0x0e…);
* reads: `ATA_ALTSTAT` is aliased onto the same resource+offset by
  `ata_default_registers()` (sys/dev/disk/nata/ata-all.c:734-735) and polled
  for status.

For the size<8 case the OOB offsets 1..7 receive command/LBA/feature bytes
(semi-attacker-influenced values once the "disk" answers IDENTIFY).

## Precondition / threat model

* Attacker must **physically insert a malicious 16-bit PCMCIA card** whose CIS
  declares `PCCARD_FUNCTION_DISK` (probe matches unconditionally,
  ata-card.c:63-67) and a first CFE I/O window of length 9..14 (or 1..7).
* CardBus (32-bit) cards do **not** go through this file — they attach via
  the PCI path (`ata-pci.c`); this file registers on the `pccard` bus only
  (ata-card.c:163).
* Impact ceiling: 1-byte port pokes at fixed offsets just past (or within a
  truncated) PCMCIA window. The bridge window is programmed with the exact
  CIS length (byte granularity), so the stray accesses stay on the host bus
  and can land in a *neighboring device's* port range (serial / FDC / PIT
  class registers live in the same allocator region). No kernel memory is
  touched; worst case is perturbation of adjacent hardware (device
  malfunction, potential system instability).

Severity: **Low** (AV:P — physical insertion required). This is a
robustness/defense-in-depth bound-validation bug, not a memory-corruption bug.

## Why untested here

The verification guest (QEMU/KVM, virtio hardware) has **no PCMCIA/cbb
controller and no slot**, so the trigger cannot be instantiated. The proof
below is a complete code-level trace from card-controlled CIS bytes to the
`bus_space_*` call. Reproducing on real hardware requires a programmable
PCMCIA card or a bridge-level emulator declaring the crafted CFE.

## Reference trigger (for hardware testers)

CIS contents needed (attribute memory):

* CISTPL_DEVICE / CISTPL_VERS_1: minimal valid card header;
* CISTPL_FUNCID: function = 4 (disk);
* CISTPL_CONFIG + CISTPL_CFTABLE_ENTRY: IO space descriptor with
  `num_iospace = 1`, window length **10** (0x09 stored, +1 by the parser),
  addr size 2 bytes, start = 0 (any);
* one IRQ line.

Expected observable: `ata_pccard_attach` succeeds; during
`ata_attach → ATA_RESET`, an outb of the control byte lands on
`window_base + 14`, i.e. 4 ports past the 10-port window owned by the card.
A logic analyzer on the host bus, or an I/O-port trap on a hypervisor that
models a pccard bridge, shows the access outside the programmed window.

## Reproduce (code-level)

Read `VERDICT.md` — it traces every hop with `path:line`:

```
CIS CFE bytes (card-controlled)
  → pccard_cis.c:1076-1092   length = tuple bytes + 1   (no 8/16 constraint)
  → pccard.c:429-449         window pre-allocated with exactly that length
  → pccard.c:1121-1149       child bus_alloc_resource returns the window
                             verbatim; requested count ATA_IOSIZE ignored
  → ata-card.c:103-106       size>8 ⇒ ATA_CONTROL at offset 14 (OOB for ≤14)
  → ata-card.c:93-96         offsets 0..7 wired unconditionally (OOB for <8)
  → ata-all.c:734-735        ATA_ALTSTAT aliased to same res+offset
  → ata-lowlevel.c           ATA_IDX_OUTB/INB ⇒ bus_space_write/read_1 at
                             window_start+14 (or +1..+7) — outside allocation
```

No build is required or possible for this pack (no target hardware in the
audit environment). `fix.diff` is the verified-by-inspection fix.
