{
  "finding_id": "DF-2497",
  "status": "reproduced",
  "verdict": "CONFIRMED. The JBOD/SPAN branch of production ata_raid_strategy() (ata-raid.c:352-353) walks rdp->disks[drv].sectors with drv++ and no bound against total_disks, MAX_DISKS (16) or the 2424-byte allocation. Driven with crafted HPTv2 SPAN metadata (production parse installs total_disks=2, disks[1].sectors=1000) and a bio at blkno=5000: (1) deterministic in-object run (test 11): the production walk read disks[2..30] -- 29 entries past the configured disks, 15 past MAX_DISKS -- then dispatched I/O with request->this(drv)=30 and request->dev = the OOB-read rdp->disks[30].dev (a single valid stopper was planted at the disks[30] stride so the walk terminates at a mapped address; every read past total_disks is production behavior); (2) past-the-allocation run (test 13): without an in-object stopper the walk continued past index 48 and reached drv=51, reading .sectors/.flags/.dev 168+ bytes past the end of the heap object out of natural kernel heap, then dispatched with that garbage dev pointer. Termination is heap-content-dependent (kernel pointers stop the walk; zero regions make it climb forever -- the filed hang variant; unmapped pages would fault), so a panic is not deterministic on this guest, but the unbounded OOB-read walk and the dispatch to an OOB-read device pointer are fully demonstrated.",
  "reproduced": true,
  "impact": "dos",
  "confidence": "certain",
  "exploit_chain": "crafted HPTv2 SPAN metadata (type=HPTV2_T_SPAN, small total_sectors) on an nata disk -> array attached with per-disk sector sum far below any bio offset -> unprivileged read/write past the array end through the ar device -> while-loop reads rdp->disks[N].sectors for unbounded N (48-byte stride over kernel heap, extent attacker-influenced via blkno) -> either stops on a heap pointer >= lba and dispatches I/O to the OOB-read .dev pointer (wild device deref in ata_raid_send_request -> panic) or climbs an all-zero region forever (hang) or hits an unmapped page (panic). Demonstrated: drv=30 (deterministic) and drv=51 (past the allocation); guest survived both because the rigs stopper/dev target was a valid fake device.",
  "evidence": "run_nonfatal.log test11 (PROOF: production strategy dispatched to disk index drv=this=30 ... the walk read 29 entries past the configured disks and 15 past disks[] bounds, parse rc=1); run_test13.log (walk reached disk index 51 ... disks[51].sectors is 168 bytes past the object, serial STRATEGY DISPATCH #1: this(drv)=51 lines); fix_validation.log (fixed kernel: dispatches=0, ar0: I/O beyond end of SPAN/JBOD array, rig_bio_done b_error=5 for both tests)",
  "kernel_refs": [
    "sys/dev/disk/nata/ata-raid.c:348-355",
    "sys/dev/disk/nata/ata-raid.c:397-411",
    "sys/dev/disk/nata/ata-raid.c:1844-1894",
    "sys/dev/disk/nata/ata-raid.h:97-110"
  ],
  "poc_changes": "No pass-1 pack existed (empty dir); the SPAN array state comes from a full production HPTv2 parse (no hand-crafted softc): the rig crafts the HPTv2 superblock (magic/order/type/disk_number/total_sectors; no checksum in that format) and lets production ata_raid_hptv2_read_meta() install it. rdp->status=AR_S_READY is set by the rig to mirror what production attach (ata_raid_config_changed) would do before I/O flows. The bio carries a rig bio_done callback so the fixed kernels biofinish(EIO) path terminates safely in the harness instead of running buf completion on a fake buffer.",
  "attempts": 4,
  "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)",
  "run_cmd": "kldload ./dfrig3_ata.ko; sysctl kern.dfrig3_ata_test=11; sysctl -n kern.dfrig3_ata_status; sysctl kern.dfrig3_ata_test=13; sysctl -n kern.dfrig3_ata_status",
  "code_hash": "bc3d761a0b7422afd5ef7908d5acc46c98c41ce5277c858e98ee5b033376407d",
  "notes": "The findings panic/leak upper bound was not forced: on this guest the walk stopped on mapped heap both times (kernel pointers exceed the test lba). The deterministic proof is the index/extent of the OOB reads plus the dispatch to the OOB-read dev pointer. Hang variant: an all-zero OOB region never terminates the loop (lba -= 0) -- consistent with the walk arithmetic; not separately demonstrated to avoid wedging the shared guest.",
  "recommended_fix": "Bound the walk: while (drv < (int)rdp->total_disks && lba >= rdp->disks[drv].sectors) ...; and fail the bio with biofinish(bp, NULL, EIO) when drv reaches total_disks (block number past the array end). 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 (walk bound + EIO in fix.diff) validates clean: both test 11 and test 13 show dispatches=0, the production ar0: I/O beyond end of SPAN/JBOD array message, and the bio completed with b_error=5 (EIO) -- the walk never leaves the configured disks; no OOB read, no dispatch to garbage dev.",
  "fix_evidence": "fix_validation.log: ===== FIXED-KERNEL TEST 11 ===== ... strategy returned 0; dispatches=0, ===== FIXED-KERNEL TEST 13 ===== ... dispatches=0, dmesg section: ar0: I/O beyond end of SPAN/JBOD array + dfrig3: rig_bio_done #1 b_error=5"
}