# DF-2494 VERDICT — reproduced (production-code rig; real-kernel BSS target)

## Bottom line

The pass-1 claim is real and worse than filed: `ata_raid_lsiv2_read_meta()`
(`sys/dev/disk/nata/ata-raid.c:2799-2879`) indexes the caller's `raidp[]`
(production callers pass the 16-entry global `ata_raid_arrays`,
`ata-raid.c:138/1477`) at `array + meta->raid_number` where `meta->raid_number`
is a raw `u_int8_t` taken from the last sector of the disk
(`LSIV2_LBA`, `ata-raid.h:456-457`). There is no bound check anywhere on the
path. On the guest this was proven three ways, all through 100% production
parser code (the only interposed seam is `ata_queue_request`, which completes
the metadata READ the way the disk would):

1. **rig test 1 (deterministic)** — `raid_number=29`: the production parser
   kmalloc()d an `ar_softc` and stored the pointer at `raidp[0+29]`, 104 bytes
   past the end of the 16-entry array; parse returned 1 (array accepted).
2. **rig test 2 (deterministic)** — `raid_number=16`: the store landed on the
   FIRST byte past the array (offset +128). In the real guest kernel that
   exact address is `&atapi_cam_devclass` (nm:
   `ata_raid_arrays=0xffffffff8114dec0`, `atapi_cam_devclass=0xffffffff8114df40`).
   With an accepted RAID type the pointer is RETAINED (only the
   unknown-RAID-type cleanup path at 2849-2850 frees/nulls it).
3. **rig test 6 (real-kernel arm)** — with `raidp` pointed at the REAL kernel
   `ata_raid_arrays` (0xffffffff8114dec0), the production skip-occupied walk
   (2802-2814: an occupied slot whose object's `format`/`magic_0` fields do not
   look like a fresh array → `continue`) walked across the real BSS neighbors
   (`atapi_cam_devclass`, `acd_/afd_/ast_devclass` all occupied) and:
   - run A (`raid_number=23`): the target slot `raidp[23]` =
     `&sysctl__hw_amr_children` was occupied (it legitimately points at the
     `hw.amr.force_sg32` oid), so the walk continued to `raidp[24]`/`raidp[25]`
     and the array landed there — real kernel BSS 192+ bytes past the array.
   - run B (`raid_number=16`): the first slot whose pointed-to heap object
     happened to pass the `format`/`magic_0` checks (`raidp[20]`, +160) got the
     parse's field writes **into that unrelated live heap object** (cross-
     subsystem type-confusion write: `magic_0=timestamp`, `type`, `total_disks`,
     `format`, `disks[2].dev/.sectors/.flags`). rc=1, guest stayed up — silent
     corruption of an unknown victim allocation.

Corroboration in the same parse function: `meta->disk_number` (u_int8_t) is
used unvalidated at `raid->disks[meta->disk_number]` (2866-2876) and
`meta->configs[raid_number]` is read with `raid_number` up to 255 against the
30-entry table (2819-2822). rig test 4 (`disk_number=49`) showed the
`disks[49]` writes landing 40 bytes past the 2424-byte `ar_softc` allocation
(readback `dev=<parent ptr> flags=0xb`); rig test 5 (`raid_number=255`) showed
the +2040 store followed by the `configs[255]` OOB read ~3.5 KB past the
512-byte metadata object (in that run the garbage `type=0` took the
unknown-type cleanup path: pointer freed, OOB slot re-nulled — a transient
write; the persistent-corruption range is `raid_number` in [16..29], and for
`raid_number` >= 30 persistence depends on adjacent-heap garbage).

## Threat model reality check

* The trigger is **on-disk crafted metadata read at subdisk attach**
  (boot or disk insertion on an nata-controlled ATA channel). No privileges
  needed in the OS — the DF-5339-style crafted-disk model.
* The **authentic boot arm could not be armed on the standard guest**:
  attaching ANY IDE hard disk (even a benign unrelated image, tried
  `usbstick.img`) to the vm.sh QEMU invocation hangs SeaBIOS before the
  loader produces a single serial byte (CPU observed HLT'd/spinning in the
  BIOS). This is an infrastructure limitation of the standard snapshot, not a
  property of the bug; the rig arm above drives the same production parser
  against the same real kernel BSS target instead.
* Practical primitive: an 8-byte kernel-heap-pointer plant at attacker-chosen
  offset +128..+2040 past `ata_raid_arrays` (skip-occupied semantics), or —
  when the landed slot already holds a heap pointer whose object passes the
  format/magic checks — silent field writes into that live object
  (type confusion). Deterministic panic was not achieved on this guest (the
  obvious victim, `sysctl__hw_amr_children`, happens to be occupied at boot,
  and `curfail.19623`'s object survived the field writes).

## Fix validation

`fix.diff` adds, right after the `$XIDE$` magic check:
`if (meta->raid_number >= MAX_ARRAYS || meta->disk_number >= MAX_DISKS) goto lsiv2_out;`
This simultaneously bounds `raidp[array + raid_number]` (array < 16),
`meta->configs[raid_number]` (< 30), and — with the companion `disk_count`
check — `raid->disks[...]`. Validated on a single-fix rebuilt kernel
(fix_build.log / fix_run.log): rig tests 1/2 return rc=0 with the witness
slots untouched, and the real-kernel test 6 leaves the real BSS neighbors
unchanged.

## Kernel references

- `sys/dev/disk/nata/ata-raid.c:2799-2806` — `raidp[array + meta->raid_number]` OOB store
- `sys/dev/disk/nata/ata-raid.c:2816` — `array += meta->raid_number` loop-counter corruption
- `sys/dev/disk/nata/ata-raid.c:2819-2822` — `meta->configs[raid_number]` OOB read (table is [30])
- `sys/dev/disk/nata/ata-raid.c:2866-2876` — `raid->disks[meta->disk_number]` OOB write
- `sys/dev/disk/nata/ata-raid.h:456-513` — `LSIV2_LBA`, `struct lsiv2_raid_conf` (raid_number/disk_number u_int8_t)
- `sys/dev/disk/nata/ata-raid.c:138` — `static struct ar_softc *ata_raid_arrays[MAX_ARRAYS]`

## 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.
