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