# DF-3690 — Adaptec HostRAID parser: unbounded `total_disks` zeroing loop smashes the kernel heap at metadata-parse time

## What this is
Evidence pack for the pass-2 finding in `sys/dev/disk/nata/ata-raid.c`
(`ata_raid_adaptec_read_meta`).

## Root cause (source)
`ata_raid_adaptec_read_meta()` takes `configs[0].total_disks` straight from
on-disk metadata (u_int16_t, big endian — up to 65535) and stores it into
`raid->total_disks` **before** the "clear out any old info" loop:

```c
1704:  raid->total_disks = be16toh(meta->configs[0].total_disks);   /* attacker: 0..65535 */
...
1716:  if (raid->generation) {                     /* TRUE: generation was just set at :1703 */
1717:      for (disk = 0; disk < raid->total_disks; disk++) {
1718:          raid->disks[disk].dev = NULL;       /* disks[] has MAX_DISKS=16 entries */
1719:          raid->disks[disk].flags = 0;
1720:      }
1721:  }
```

`struct ar_disk raid->disks[MAX_DISKS]` is 16 entries inside a
`kmalloc(sizeof(struct ar_softc))` object. With `total_disks = 60000` the loop
writes `NULL`/`0` every 48 bytes across a ~2.8 MB span of kernel heap starting
inside the live `ar_softc` — purely attacker-chosen length, deterministic, at
subdisk-attach time (boot or disk attach). No checksum guards the Adaptec
format (only two magic constants), and `raid->generation` is set from the same
metadata at :1703 *before* it is tested at :1716, so even the *first* parse of
a fresh array slot takes the loop.

## Reproduce
1. `python3 mkmeta.py` (host) builds `f1_adaptec.img`: 262080-sector raw disk
   (multiple of 16*63) with a valid Adaptec header (magic_0/magic_3 as raw
   LE compares, generation=1 as be32) and `configs[0].total_disks = 60000`.
2. Attach the image as `ata0-master` at QEMU launch
   (`ARAID_DRIVE=... dfbsd-qemu/vm.sh up` — see run.sh).
3. Boot the DragonFly guest.

## Expected output (baseline, unpatched kernel)
```
ata0: <ATA channel 0> on atapci0
panic: vm_fault: fault on stack guard, addr: 0xfffff8008df60000
...
ata_raid_subdisk_attach() at ata_raid_subdisk_attach+0x42b
Debugger("panic")
```
The zeroing loop ran ~2.8 MB past the ar_softc allocation and hit a kernel
thread stack guard page → fatal kernel-mode page fault while parsing the
crafted disk. Captured in `panic.txt`.

With the fix applied (fix.diff: reject `total_disks > MAX_DISKS`) the same
disk yields `Adaptec total_disks out of range`, no array, normal boot.

## Files
- `mkmeta.py`     metadata image builder (host, python3)
- `f1_adaptec.img` not stored (134 MB); regenerate with mkmeta.py
- `run.sh`        exact commands used
- `panic.txt`     serial console capture of the panic (baseline)
- `run.patched.log` re-run on the fixed kernel
- `fix.diff`      combined pass-2 fix (this finding = hunk `total_disks out of range`)
- `verdict.json`  machine verdict
