DF-2496 / verdict.json
{ "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" } |