DF-2513 / verdict.json
{ "finding_id": "DF-2513", "status": "reproduced", "reproduced": 1, "impact": "corruption", "confidence": "certain", "verdict": "The verbatim nvme_admin_state_identify_ns computes nsc->blksize = 1 << lbafmt->sect_size (nvme_admin.c:673-675) with sect_size a device-controlled uint8_t from the Identify Namespace DMA and no range check. The rig refills the crafted Identify response at the DMA-completion boundary (after the drivers bzero of req->info - the first version filled too early and the bzero wiped it). Results: sect_size=9 -> 512 (legal control); 31 -> 0x80000000 (2GiB blocks); 36 -> 16 (shift count masked to 5 bits - C11 UB); 63 -> 0x80000000. The garbage blksize is what nvme_disk_attach publishes as the namespace media block size (geometry/media-size corruption; divisor in I/O sizing). Fixed source (reject formats outside 9..12, skip attach) A/B: attach count 0, no garbage published.", "exploit_chain": "hostile/faulty NVMe controller (PCI trust); geometry corruption; no memory-safety violation by itself.", "evidence": "run.log: blksize 512/2147483648/16/2147483648 for sect_size 9/31/36/63; fixed: attach skipped.", "kernel_refs": [ "sys/dev/disk/nvme/nvme_admin.c:673", "sys/dev/disk/nvme/nvme_admin.c:675", "sys/dev/disk/nvme/nvme_ident.h:234", "sys/dev/disk/nvme/nvme_disk.c" ], "poc_changes": "DMA refill hook moved into the interposed wait/completion (bzero ordering); first rig version was self-defeating.", "attempts": 2, "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": 500, "guest_dirty": 0, "build_cmd": "sh /root/rigs/dfv_nvme/gen.sh && make -m /usr/src/share/mk (in dfv_nvme)", "run_cmd": "kldload dfv_nvme.ko; for s in 9 31 36 63; do sysctl kern.dfv_nvme_run=$s >/dev/null; sysctl -n kern.dfv_nvme_status; done", "code_hash": "df5fe33593aad934bb0be9166c045695e2852eeb26bfd94e3b3b4ddb54a4d742", "notes": "shift-by->31 is C11 UB; on this target the count masks to 5 bits so the device selects any 1<<(n&31); sect_size=31 -> 0x80000000. Fixed code rejects formats outside 9..12 (attach skipped, count 0).", "recommended_fix": "validate lbafmt->sect_size (9..12) before the shift", "fix_status": "fixed" } |