# DF-1174 — `ata_raid_attach` / parsers divide-by-zero

## Finding

`ata_raid_attach()` at `sys/dev/disk/nata/ata-raid.c:144` sanitises
`rdp->total_sectors` at lines 157-158:

```c
rdp->total_sectors = rounddown(rdp->total_sectors,
    rdp->interleave * rdp->width);
```

where `rounddown(x, y) == ((x)/(y))*(y)` (`sys/sys/param.h:400`). If either
`rdp->interleave` or `rdp->width` is 0 — both are populated **directly from
disk metadata with no validation** in multiple RAID metadata parsers — the
kernel performs an integer division by zero (#DE on x86) and panics.

This only fires for `AR_T_RAID0 | AR_T_RAID01 | AR_T_RAID5` arrays, but the
parsers will happily set those types alongside zero width/interleave:

| Parser     | interleave source                          | width source                              |
|------------|--------------------------------------------|-------------------------------------------|
| HPTv2      | n/a (sets only `width`)                    | `:1868` `meta->array_width`               |
| HPTv3      | n/a                                        | `:2039/2045/2051` `meta->configs[0].total_disks` |
| Intel      | `:2229` `map->stripe_sectors`              | derived from disk count                   |
| ITE        | `:2528` `meta->stripe_sectors`             | derived from disk count                   |
| nVidia     | `:3107` `meta->stripe_sectors`             | `:3108` `meta->array_width`               |
| SII        | `:3562` `meta->stripe_sectors`             | `:3563` `(meta->raid0_disks!=0xff)?...:1` |

A second div-by-zero exists at `:1028`:
```c
status->progress = 100 * rdp->rebuild_lba / rdp->total_sectors;
```
triggered via `IOCATARAIDSTATUS` on an array whose `total_sectors` was set to
0 by crafted metadata.

## Reproducibility on the audit guest

- `nataraid` is `optional` (`sys/conf/files:143`), not compiled into
  `X86_64_GENERIC` (`#0` kernel). The module ships at
  `/boot/kernel/nataraid.ko` (loadable, but the `nata` controller stack is
  opt-in and not loaded on the audit guest).
- The QEMU guest uses **virtio-blk** (`vtblk0`), not ATA. The `ata-raid`
  metadata parsers only run when the `nata` controller driver probes a disk,
  which requires an actual ATA/SATA controller. Live triggering would require
  rebooting QEMU with an IDE disk image containing crafted metadata — outside
  the scope of "guest already up".

A userspace harness (`harness.c`) reconstructs `rounddown()` and the
`ata_raid_attach` line 157-158 logic and demonstrates the divide-by-zero
(SIGFPE in userspace, equivalent to kernel #DE panic) for several crafted
parser inputs. It also demonstrates the `IOCATARAIDSTATUS` div-by-zero at
line 1028.

## How to run the harness

```
./build.sh
./run.sh
```

Expected:
```
[case 1] nVidia meta stripe_sectors=0, array_width=2 -> rounddown(...)
    BUG: integer divide-by-zero -> SIGFPE 8 (kernel equivalent: #DE trap -> panic in ata_raid_attach at ata-raid.c:157-158)
...
[case 4] IOCATARAIDSTATUS: total_sectors=0 (crafted metadata)
    BUG: integer divide-by-zero -> SIGFPE 8 (kernel equivalent: #DE trap -> panic in ata_raid_status at ata-raid.c:1028)
```

## Threat model

The metadata is read from the first/last sectors of a physical disk during
the nata attach probe. An attacker who can supply a disk with crafted RAID
metadata (USB/external disk, mounted image presented as ATA, malicious VM
disk) triggers an automatic kernel panic at the next driver probe. No
privilege required beyond the ability to write to a disk that the kernel
subsequently probes.

Severity Medium: requires the nata stack to actually be in use (DragonFlyBSD's
default ATA stack is `sys/dev/disk/ata`, the newer `nata` is opt-in); not in
GENERIC. High (DoS) for systems that load `nata`/`nataraid`.

## Recommended fix

Validate `width != 0 && interleave != 0` before the `rounddown` in
`ata_raid_attach`, and validate `total_sectors != 0` before the
`IOCATARAIDSTATUS` division. See `fix.diff`.
