Systemic divide-by-zero via zero width or interleave from crafted metadata
Summary
ata_raid_attach at :157-158: rounddown(total_sectors, interleave*width) expands to (x/(i*w))*(i*w). If either is 0 from crafted metadata -> kernel #DE panic. Affects AR_T_RAID0/RAID01/RAID5. Multiple parsers set width/interleave from disk fields without zero check: HPTv2 :1868, nVidia :3107-3108, SII :3562, Intel :2229, ITE :2528. Also IOCATARAIDSTATUS :1028 divides by total_sectors. Attacker: crafted disk. Automatic at attach. Fix: validate width!=0 and interleave!=0 before attach.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1174 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| harness.c | trigger-source | userspace reconstruction of ata_raid_attach rounddown + IOCATARAIDSTATUS div-by-zero | 3.4 KB | view raw |
| build.sh | build-script | cc -O2 -Wall -o harness harness.c | 212 B | view raw |
| run.sh | run-script | ./harness | 71 B | view raw |
| build.log | build-log | successful in-guest build | 86 B | view raw |
| run.log | run-log | harness output: 4 SIGFPE div-by-zero cases | 897 B | view raw |
| env.txt | environment | uname, cc version, nataraid.ko location, module-build validation summary | 654 B | view raw |
| fix.diff | suggested-fix | guard interleave*width and total_sectors divisions | 1.3 KB | view raw |
| VERDICT.md | verdict | full narrative + module-build validation | 4.1 KB | β raw |
| README.md | readme | human-facing summary | 3.9 KB | β raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
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:
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:
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
nataraidisoptional(sys/conf/files:143), not compiled intoX86_64_GENERIC(#0kernel). The module ships at/boot/kernel/nataraid.ko(loadable, but thenatacontroller stack is opt-in and not loaded on the audit guest).- The QEMU guest uses virtio-blk (
vtblk0), not ATA. Theata-raidmetadata parsers only run when thenatacontroller 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.
DF-1174 β VERDICT
Verdict: REPRODUCED (source+harness, module-build-validated). Not uid=0 β
this is a divide-by-zero DoS in optional legacy ATA RAID metadata parsing.
Bug confirmation
ata_raid_attach() at sys/dev/disk/nata/ata-raid.c:144 runs (lines 154-160):
if (rdp->type == AR_T_RAID0 || rdp->type == AR_T_RAID01 || rdp->type == AR_T_RAID5) {
rdp->total_sectors = rounddown(rdp->total_sectors,
rdp->interleave * rdp->width);
...
}
rounddown(x, y) is ((x)/(y))*(y) (sys/sys/param.h:400). If
interleave == 0 or width == 0 (their product), the kernel executes an
integer divide-by-zero β #DE trap β kernel panic.
Both fields are populated directly from disk metadata with no zero check across six RAID metadata parsers in the same file:
| Parser | line | field set |
|---|---|---|
| HPTv2 | :1868 |
width = meta->array_width |
| HPTv3 | :2039/2045/2051 |
width = meta->configs[0].total_disks |
| Intel | :2229 |
interleave = map->stripe_sectors |
| ITE | :2528 |
interleave = meta->stripe_sectors |
| nVidia | :3107-3108 |
interleave = meta->stripe_sectors; width = meta->array_width |
| SII | :3562 |
interleave = meta->stripe_sectors |
A second div-by-zero exists at :1028 (ata_raid_status):
status->progress = 100 * rdp->rebuild_lba / rdp->total_sectors;
triggered via IOCATARAIDSTATUS when total_sectors == 0 from crafted metadata.
Reproducibility on the audit guest
nataraidisoptional(sys/conf/files:143); not inX86_64_GENERIC. The module ships at/boot/kernel/nataraid.ko(loadable, but the legacynatacontroller stack is opt-in and not loaded on the audit guest).- The QEMU guest uses virtio-blk (
vtblk0), not ATA, so theata-raidmetadata parsers are never invoked on guest disks. Live triggering would require rebooting QEMU with an added IDE/SATA disk image containing crafted metadata β outside the "guest already up" envelope.
A userspace harness (harness.c) reconstructs rounddown() and replays the
ata_raid_attach line 157-158 division and the ata_raid_status line 1028
division for several crafted-metadata cases. All produce SIGFPE (integer
divide-by-zero), the userspace analogue of the kernel #DE panic:
[case 1] nVidia meta stripe_sectors=0, array_width=2 -> rounddown(1000000, 0*2)
BUG: integer divide-by-zero -> SIGFPE 8
[case 2] Intel meta stripe_sectors=64, array_width=0 -> rounddown(1000000, 64*0)
BUG: integer divide-by-zero -> SIGFPE 8
[case 3] ITE meta stripe_sectors=0, array_width=0 -> rounddown(1000000, 0*0)
BUG: integer divide-by-zero -> SIGFPE 8
[case 4] IOCATARAIDSTATUS: total_sectors=0 (crafted metadata)
BUG: integer divide-by-zero -> SIGFPE 8
Module build validation
The fix.diff was applied in-guest to /usr/src and make in
/usr/src/sys/dev/disk/nata/nataraid produced a clean nataraid.ko (RC=0,
no warnings, full -Werror kernel CFLAGS). The patched source compiles. After
validation the source was reverted (patch -p1 -R).
Exploit chain
none β divide-by-zero is a pure DoS, not a memory-corruption primitive.
Fix
fix.diff:
1. In ata_raid_attach, before the rounddown, validate
interleave != 0 && width != 0; log and bail out otherwise.
2. In ata_raid_status (IOCATARAIDSTATUS), guard the total_sectors
division with a non-zero check.
git apply --check passes against the audit tree, and the patched source
compiles cleanly into nataraid.ko on the guest.
Threat model
The metadata is parsed from the first/last sectors of a physical disk during
the nata controller's attach probe. An attacker who can supply a disk with
crafted RAID metadata (USB/external disk, malicious VM disk image, mounted
image) 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 legacy nata stack (sys/dev/disk/nata/,
opt-in; DragonFlyBSD's default ATA stack is sys/dev/disk/ata). High (DoS)
for systems that load nata/nataraid.
Fix verification
not_testablecompile+harness validated
module/object build rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source+harness. nata ata_raid_attach rounddown(,interleavewidth) div-by-zero. Not AMD GPU - nataraid. Module build validated.
No comments yet.