⬢ DragonFlyBSD Kernel Audit
DF-2498 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-2498",
  "status": "reproduced",
  "verdict": "CONFIRMED, both halves of the finding, through the unmodified production ata_raid_intel_read_meta() fed crafted Intel MatrixRAID metadata with a valid checksum. OOB WRITE: map->total_disks=60 is taken verbatim as raid->total_disks (ata-raid.c:2230; MAX_DISKS=16), so the fill loop (:2243-2263) wrote raid->disks[16..59] -- 44 entries past disks[MAX_DISKS-1] IN-OBJECT (smashing toggle/rebuild_lba/lock/disk/devstat/cdev/pid of the live 2424-byte ar_softc; readback shows the crafted dev/serial=D0../sectors=0xC0FFEE/flags=0xb pattern) and 11/11 entries disks[49..59] PAST the allocation (crafted 0xC0FFEE markers read back at victim+2488..+2992; an adjacent 0x11-filled guard chunk of the same size class was smashed across 324/2424 bytes with the same pattern). The serial-match loop (:2266-2283) adds OOB .dev=parent/.flags writes for the same entries. With map->total_disks=255 (also unvalidated) the range extends to ~+13.3KB past the object. OOB READ: disk_idx = map->disk_idx[disk] & 0xffff with 0xffff makes meta->disk[disk_idx] read at meta+216+48*65535 = meta+3145996, ~3.0MB past the 1536-byte metadata kmalloc; proven executed via a mixed-index craft (in-bounds 0xE0 marker honored, wild sibling reads performed, bytes landed zeroed-heap so no fault -- the kernel direct-maps kmem, making the panic variant heap-layout-dependent). No userland leak path demonstrated (serials are not exposed by IOCATARAIDSTATUS): demonstrated impact is silent kernel-heap corruption, same profile as DF-2494 in this parser family.",
  "reproduced": true,
  "impact": "none",
  "confidence": "certain",
  "exploit_chain": "crafted Intel metadata (Intel Raid ISM Cfg Sig. , valid checksum, meta->total_disks=2 keeping the mapping in-bounds, map->total_disks=60..255, disk_idx[] arbitrary 16-bit) on a disk attached to an nata channel -> production parse at boot/attach -> (a) raid->disks[disk] written for disk up to 255: attacker-chosen 16-byte serials, sectors and flags planted at 48-byte stride up to ~13.3KB past a 2424-byte heap object (in-object fields toggle/rebuild_lba/lock/disk/devstat/cdev/pid of a LIVE array corrupted first); (b) 16-byte heap samples readable from meta+216+48*idx for any 16-bit idx (up to ~3.0MB away) copied into array state. Silent corruption of adjacent kernel heap allocations; a deterministic panic was NOT achieved on this guest (writes land in mapped neighbors; the wild read lands in mapped KVA) -- primitive, not a finished escalation.",
  "evidence": "run_nonfatal.log test10 (in-object proof: disks[16].serial[0..1]=d0d1 == crafted D0 D1 -- OOB WRITE CONFIRMED, past-object proof: 11/11 entries disks[49..59] carry the crafted sectors marker 0xC0FFEE at rdp+2488..+2992 (object ends at +2424), guard[33] ... SMASHED (324/2424 bytes): first words ffffffff8260b440 d7d6d5d4d3d2d1d0 ...); run_test23_24.log (wild read at meta+3145996 executed; loop-ran marker e0e1 CONFIRMED with 0xffff siblings reading zeroed heap 3.0MB away, no fault); fix_validation.log (fixed kernel: ataraid: Intel invalid map total_disks 60, parse rejected, guards untouched; disks[1..15] stay zero -- no wild read)",
  "kernel_refs": [
    "sys/dev/disk/nata/ata-raid.c:2165",
    "sys/dev/disk/nata/ata-raid.c:2190",
    "sys/dev/disk/nata/ata-raid.c:2203",
    "sys/dev/disk/nata/ata-raid.c:2230",
    "sys/dev/disk/nata/ata-raid.c:2243-2263",
    "sys/dev/disk/nata/ata-raid.c:2266-2283",
    "sys/dev/disk/nata/ata-raid.h:295-354"
  ],
  "poc_changes": "No pass-1 pack existed (empty dir); rig authored on the DF-2494 pattern. The Intel craft required: correct 1024-byte checksum over the post-half-swap buffer (computed in-kernel), the parsers half-swap inverted in the image builder, meta->disk[0].serial matching the fake parent identify serial so the array is retained for readback, and meta->disk[1].serial = 0xE0.. as an in-bounds loop-ran marker. The +3MB read lands in mapped KVA on this guest (no fault) -- demonstrated via the mixed disk_idx marker technique instead of a crash. Impact recorded as none only because the enum lacks a silent-corruption value; see impact_note in manifest.json.",
  "attempts": 5,
  "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=10; sysctl -n kern.dfrig3_ata_status; sysctl kern.dfrig3_ata_test=24; sysctl -n kern.dfrig3_ata_status",
  "code_hash": "bc3d761a0b7422afd5ef7908d5acc46c98c41ce5277c858e98ee5b033376407d",
  "notes": "Guard-sandwich adjacency for the 2424-byte size class is unreliable on this allocator (chunks are scattered), so the past-allocation write is proven by direct readback of the crafted markers past the object end (deterministic) plus one run where a guard chunk happened to be adjacent (smashed). The map-past-buffer clause (meta->total_disks > ~21) is covered by the same fix; on the guest such reads stay in mapped KVA.",
  "recommended_fix": "In ata_raid_intel_read_meta(): reject meta->total_disks outside [1, MAX_DISKS] before computing map; reject map->total_disks outside [1, MAX_DISKS] before use; skip any disk_idx >= meta->total_disks. 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 (three bounds checks in the Intel parser) validates clean: the crafted map->total_disks=60 parse is rejected (ataraid: Intel invalid map total_disks 60, rc=0, raidp[0] freed+NULL, guards untouched, no in-object or past-allocation writes); disk_idx=0xffff entries are skipped (disks[1..15] remain zero -- the ~3.0MB wild read is gone) while the in-bounds marker still parses; guest stable through the whole battery.",
  "fix_evidence": "fix_validation.log: ===== FIXED-KERNEL TEST 10 ===== ... parse rc=0 raidp[0]=0 ... parse failed (rejected before any loop writes; guard list unchanged), ===== FIXED-KERNEL TEST 24 ===== ... disks[1..15] all zero, loop-ran marker e0e1 CONFIRMED, dmesg: ataraid: Intel invalid map total_disks 60"
}