DF-4009 / verdict.json
{ "finding_id": "DF-4009", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "likely", "verdict": "Mechanism certain at source level, hardware-gated live: MPTIOCTL_PASS_THRU with Function=0x33 (NVME_ENCAPSULATED, user-controlled byte) feeds raw user u32 DataSize/DataOutSize into mpr_build_nvme_prp() whose while(length) walk (mpr.c:2757) writes 8-byte PRP entries linearly through the 8 MiB contiguous PRP arena with no end check, before any firmware interaction. For DataSize=0xFFFFFFFF the walk needs 1,050,625 entries (8,405,000 bytes) vs a 1,048,576-entry arena -> 16,416 bytes of OOB write even from chunk 0, scaling to ~8.2 MB when the first allocated PRP chunk sits near the arena end; kmalloc failure for the 4 GB cm_data is NOT checked (mpr_user.c:908) and does not gate the walk (paddr=0 variant). Written values are semi-controlled (0- or KVA-based incrementing dwords in 8-byte steps). Not live-reproduced: no mpr controller on the guest; the walk/arena arithmetic is replicated verbatim on-guest (prp_overwalk).", "exploit_chain": "operator-group ioctl user -> MPTIOCTL_PASS_THRU {Function=0x33, DataDirection=READ, DataSize=0xFFFFFFFF} -> kmalloc(4GB)=NULL unchecked (mpr_user.c:908) -> mpr_build_nvme_prp(..., NULL, 0xFFFFFFFF, 0) at mpr_user.c:970 -> 1M+ entry linear write marching past the 8MiB DMA arena into adjacent kernel memory with attacker-chosen length. Requires an mpr-attached controller; the OOB write itself needs no FW cooperation (occurs before mpr_wait_command).", "evidence": "replication_run.log (OOB distances for DataSize x start_chunk incl. 16416 bytes from chunk 0 and 8208416 bytes from chunk 2000), prp_overwalk.c (verbatim walk/arena arithmetic), VERDICT.md", "kernel_refs": [ "sys/dev/raid/mpr/mpr_user.c:904", "sys/dev/raid/mpr/mpr_user.c:908", "sys/dev/raid/mpr/mpr_user.c:945", "sys/dev/raid/mpr/mpr_user.c:970", "sys/dev/raid/mpr/mpr.c:1593", "sys/dev/raid/mpr/mpr.c:2680", "sys/dev/raid/mpr/mpr.c:2716", "sys/dev/raid/mpr/mpr.c:2750", "sys/dev/raid/mpr/mpr.c:2757", "sys/dev/raid/mpr/mprvar.h:40", "sys/dev/raid/mpr/mprvar.h:59" ], "poc_changes": "n/a (new finding; replication authored during pass 2)", "attempts": 1, "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": 60, "guest_dirty": 0, "build_cmd": "cd /tmp && cc -O2 -Wall -o prp_overwalk prp_overwalk.c", "run_cmd": "/tmp/prp_overwalk", "code_hash": "sha256: see prp_overwalk.c", "notes": "Privileged surface (operator group). Related observations in the same path recorded in the audit negative notes: mpr.c:2750 uses the kernel VIRTUAL address as a physical PRP address (wild-DMA shape, upstream-shared), and the walks first allocation KASSERTs on exhaustion (mpr.c:2717). High severity on real hardware: deterministic attacker-length OOB write into kernel memory.", "recommended_fix": "Validate DataSize/DataOutSize <= sc->maxio before the NVMe branch (mpr_user.c:945), check the kmalloc result at :908, and bound the PRP walk against the arena end in mpr_build_nvme_prp().", "fix_status": "untested", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff not yet authored (see README.md fix sketch)", "fix_evidence": "" } |