β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-1174

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1174 Β· 11 files
FileTypeDescriptionSize
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
README.md readme human-facing summary
↓ download 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

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

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

VERDICT.md verdict full narrative + module-build validation
↓ download raw

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

  • nataraid is optional (sys/conf/files:143); not in X86_64_GENERIC. The module ships at /boot/kernel/nataraid.ko (loadable, but the legacy nata controller stack is opt-in and not loaded on the audit guest).
  • The QEMU guest uses virtio-blk (vtblk0), not ATA, so the ata-raid metadata 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_testable

compile+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.