⬢ DragonFlyBSD Kernel Audit
DF-2496 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-2496",
  "status": "reproduced",
  "verdict": "CONFIRMED at all three filed divide sites, each driven to a deterministic Fatal trap 18 (integer divide fault) on the audit guest, one per boot. (1) strategy: production Intel parse (rig test 20) installs interleave=0 from crafted stripe_sectors; production ata_raid_strategy() executes blkno % rdp->interleave (ata-raid.c:359) -> Stopped at ata_raid_strategy+0x2f3: divq %r12,%eax. (2) status via REAL kernel: rig parse plants total_sectors=0 into the REAL ata_raid_arrays[0] (0xffffffff8114dec0) and a real userland ioctl(IOCATARAIDSTATUS) on /dev/ata executes the REAL built-in kernels status divide (ata-raid.c:1028) -> Stopped at ata_raid_ioctl+0x11b: divq 0x48(%r14),%eax (IP 0xffffffff803388eb = kernel built-in, full syscall->ioctl->divide chain). (3) attach: crafted heads=0 JBOD geometry -> production ata_raid_attach() divides total_sectors/(heads*sectors)==0 (ata-raid.c:181) -> Fatal trap 18 divq (function inlined at -O2). Setup half (test 12): the production Intel parse accepts stripe_sectors=0 AND total_sectors=0 and installs a READY RAID0 array -- no parser-side validation exists. RAID5 variants (:366-372) are the same class and covered by the same fix guard.",
  "reproduced": true,
  "impact": "panic",
  "confidence": "certain",
  "exploit_chain": "crafted Intel/HPTv2 RAID metadata on a disk attached to an nata channel -> production parser copies geometry verbatim (interleave=0 / total_sectors=0 / heads=0, array AR_S_READY) -> first I/O through the ar device (strategy), any IOCATARAIDSTATUS on /dev/ata (root-only device), or array attach executes the unguarded divide -> Fatal trap 18, kernel dead at db> (local/attach-time DoS; status ioctl requires root, I/O path requires only a mounted array). No memory-corruption primitive: impact is denial of service.",
  "evidence": "panic_strategy.txt (Fatal trap 18, ata_raid_strategy+0x2f3 divq); panic_status_ioctl.txt (Fatal trap 18, ata_raid_ioctl+0x11b divq, real kernel IP, current process = atastats syscall); panic_attach.txt (Fatal trap 18 divq, attach inlined); run_nonfatal.log test12 (interleave=0 and total_sectors=0 installed verbatim, rc=1); fix_validation.log (tests 20/21/22 all survive on the single-fix kernel: ar0: degenerate RAID0/01 geometry (interleave=0 width=2), rig_bio_done b_error=5, attach completes, atastat returns ok ... progress=0)",
  "kernel_refs": [
    "sys/dev/disk/nata/ata-raid.c:359-361",
    "sys/dev/disk/nata/ata-raid.c:366-373",
    "sys/dev/disk/nata/ata-raid.c:1028",
    "sys/dev/disk/nata/ata-raid.c:157-158",
    "sys/dev/disk/nata/ata-raid.c:181",
    "sys/dev/disk/nata/ata-raid.c:2229",
    "sys/dev/disk/nata/ata-raid.c:2231"
  ],
  "poc_changes": "No pass-1 pack existed (empty dir); rig authored fresh on the proven DF-2494 pattern: the UNMODIFIED production sys/dev/disk/nata/ata-raid.c is #included into a KLD (dfrig3_ata.ko) with only ata_queue_request interposed (serves the crafted metadata sector and logs strategy dispatches). Crafted Intel metadata requires a valid checksum over the post-half-swap buffer (computed in-kernel by the rig builder) and a serial-matching fake parent identify block so the parsed array is retained. Fixed two rig bugs found during runs: %p format on u_int64 (build error) and a NULL kfree in test 10 cleanup that panicked the FIRST fixed-kernel validation boot (rig bug, not a fix bug; re-validated cleanly).",
  "attempts": 9,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul  2 06:02:54 UTC 2026     root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "runtime_sec": 4300,
  "guest_dirty": 0,
  "build_cmd": "guest /root/dfrig3: ./build.sh  (make KLD dfrig3_ata.ko from #included production ata-raid.c + cc atastat.c)",
  "run_cmd": "kldload ./dfrig3_ata.ko; sysctl kern.dfrig3_ata_test=12 (setup); one per boot: =20 (strategy panic), =21 then /root/dfrig3/atastat 0 (real-ioctl panic), =22 (attach panic); read kern.dfrig3_ata_status",
  "code_hash": "bc3d761a0b7422afd5ef7908d5acc46c98c41ce5277c858e98ee5b033376407d",
  "notes": "Panic runs each require their own boot (vm.sh reset with-src between). The status site was proven through the REAL kernels built-in ataraid + a real userland ioctl (the strongest chain); strategy and attach through the rigs identical production code. Boot-time attach of real IDE disks is blocked on this QEMU snapshot (SeaBIOS hang, documented in DF-2494), so the authentic boot arm is infrastructure-limited, not code-limited.",
  "recommended_fix": "Validate geometry before use: guard strategy RAID0/01 (!interleave || !width) and RAID5 (!interleave || width < 2) with biofinish(EIO); make status->progress conditional on total_sectors != 0; guard the attach rounddown divisor and heads*sectors divisor (see fix.diff).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_kernel_uname": "DragonFly 6.5-DEVELOPMENT #1: Wed Oct 14 20:11:51 UTC 2026     root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC",
  "fix_verdict": "Single-fix kernel (fix.diff applied to guest /usr/src, nativekernel + installkernel, rig rebuilt against the fixed source) eliminates all three panics: strategy returns EIO via the degenerate-geometry guard, attach completes (d_ncylinders=0), and the previously-fatal IOCATARAIDSTATUS returns ok ... progress=0. Baseline 3/3 fatal, patched 0/3, guest stable.",
  "fix_evidence": "fix_validation.log: ===== FIXED-KERNEL TEST 20 ===== ... SURVIVED, ar0: degenerate RAID0/01 geometry (interleave=0 width=2), rig_bio_done #1 b_error=5, ===== FIXED-KERNEL TEST 21 + real ioctl ===== ... atastat: ok type=4 total_disks=2 interleave=64 status=1 progress=0, test22 SURVIVED -- on a FIXED kernel this is the expected outcome"
}