# DF-3691 VERDICT

**Status: reproduced** (panic; deterministic at boot from one crafted disk).

## Reproduction
Crafted FreeBSD/Promise RAID0 metadata (checksum-valid, single online disk,
`total_disks=1`, `interleave=1`, `width=64`) on `ata0-master`:
the array assembled READY at boot (`ar0: 48MB <FreeBSD PseudoRAID RAID0> status:
READY`), then the kernel's own slice-scan I/O drove
`drv = (blkno / interleave) % width = blkno % 64` past `disks[15]`:

```
Fatal trap 12: page fault while in kernel mode
fault virtual address = 0x80
current process = Idle
Stopped at device_get_softc: movq 0x80(%rdi),%rax
```

`0x80 = offsetof(struct device, softc)`; `%rdi` = `request->dev` =
`rdp->disks[drv].dev` read OOB from zeroed heap ⇒ NULL. The exact sink is
`sys/dev/disk/nata/ata-raid.c:400-410` (flags OOB read passes because zeros
don't look PRESENT|ONLINE) → `:409` `request->dev = rdp->disks[request->this].dev`
→ `:410` `ata_raid_send_request` → `device_get_softc(NULL)`.

## Primitive characterization
- **RAID0/RAID5** (demonstrated): user-selected LBA picks `drv` in
  [0,width); OOB *read* of a `device_t` at `ar_softc + disks_off + drv*48`
  used as a pointer → wild/fake-device deref. With heap grooming this is a
  type-confused `struct ata_device *` (max_iosize/mode/unit consumed, request
  queued through `device_get_parent(dev)` chain) — an exploitation path that
  was *not* pursued to uid0 in this run (baseline heap behind ar_softc was
  zeros ⇒ clean NULL-deref panic).
- **RAID1/RAID01** (source-proven, same root cause): OOB *writes* —
  `rdp->disks[drv+width].last_lba = blkno + chunk` (`:567/:587`, user-chosen
  64-bit value at `disks + width*48 + 32`) and
  `composite->request[this] = <ata_request *>` (`:504-505/:562-563`, kernel
  pointer written up to `request[2*width-1]`, array is 32). These require the
  OOB flags word at `disks[width].flags` to have AR_DF_ONLINE (heap-groom
  dependent), which is why the RAID0 read path was used for the deterministic
  demonstration.
- Width sources include a **16-bit** field (Adaptec `be16toh(total_disks)`,
  up to 65535 → `drv` up to 65534, ~3.1 MB away from `disks[]`).

## Why READY with width=64 passes sanity checks
`ata_raid_config_changed` loops `disk < rdp->total_disks` (=1, online) for
RAID0; nothing cross-checks `width` vs `total_disks`/MAX_DISKS anywhere.
`ata_raid_attach:157` rounds total_sectors down by `interleave*width` but
never validates width.

## Fix validation
fix.diff adds a geometry guard at the top of `ata_raid_strategy`
(width <1 or >MAX_DISKS; RAID1/01 2*width >MAX_DISKS; RAID5 width<2 →
biofinish EIO) and clamps the RAID1/01 loop in `ata_raid_config_changed`.
Patched kernel: same disk boots, array still announced READY at attach
(status logic for total_disks=1 RAID0 is unchanged), but all I/O returns EIO
("illegal array geometry (width 64)" on first request), boot completes to
login, no fault. `run.patched.log`. fix_status = fixed.

## Distinction from known findings
DF-1174/DF-2496 cover *divide-by-zero* from zero width/interleave.
DF-1173/DF-2493/DF-2497 cover `disk_number`/`total_disks` indexing and the
SPAN/JBOD walk. **No prior finding covers `width`-driven indexing in the
RAID0/1/01/5 strategy, `config_changed`, `done`, or the composite
`request[]` writes.**

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
