DF-5010 / verdict.json
{ "finding_id": "DF-5010", "verdict": "not_reproduced", "impact": "none", "confidence": "certain", "verdict_paragraph": "vkd_doio() genuinely lacks any bounds validation of bio_offset/b_bcount against sc->size (latent arbitrary-offset OOB bcopy in COW mode / backing-file sparse growth in regular mode), but every currently reachable path into vkdstrategy() is bounds-checked by the generic slice layer first: all devfs-visible vkd nodes are disk-core covering devices routed through dscheck(), which rejects/clamps O/OB bios (subr_diskslice.c:253-277); the only dscheck bypass (raw partitions >=128, subr_diskslice.c:171-175) requires dev_t minors vkd never creates; diskdump() bounds-checks separately. Verified empirically: a full vkernel64 was built inside the DF QEMU guest, booted with -R (COW) and plain disks, and 512-aligned OOB raw reads/writes at 1TB and size+512 issued as guest root (PID1). All were silently rejected; the vkernel host process survived every attempt (a wild COW bcopy would have kernel-faulted before init exited) and the backing files never grew. Filed as Info hardening: fd.c demonstrates the in-tree precedent for dscheck-bypassing raw partitions; the day vkd gains them, COW mode becomes guest-root -> host-process memory corruption. Fix validated (no regression, driver-level rejection armed).", "exploit_chain": "", "evidence": "run.log (run1: dscheck alignment rejections printed for /dev/vkd1,/dev/vkd2), run.2.log + run.3.log (aligned OOB: no fault, init exit 0, plain.img unchanged at 16777216 bytes), run.4-fixed.log (patched kernel: identical clean behaviour), VERDICT.md (full path analysis), build.log (vkernel build incl. HEAD link breakage of vkernel64)", "kernel_refs": [ "sys/dev/virtual/vkernel/disk/vdisk.c:303-347", "sys/dev/virtual/vkernel/disk/vdisk.c:310-314", "sys/dev/virtual/vkernel/disk/vdisk.c:322-326", "sys/dev/virtual/vkernel/disk/vdisk.c:346", "sys/kern/subr_diskslice.c:91-304", "sys/kern/subr_diskslice.c:171-175", "sys/kern/subr_diskslice.c:253-277", "sys/kern/subr_disk.c:691-745", "sys/kern/subr_disk.c:1240-1246", "sys/kern/kern_physio.c:69", "sys/dev/disk/fd/fd.c:1036-1054", "sys/platform/pc64/x86_64/support.s:247-249" ], "poc_changes": "Initial seed sketch had wrong syscall expectations (4-byte unaligned raw I/O, which dscheck rejects on alignment before bounds). Final trigger uses 512-byte aligned pwrite/pread on the whole-disk nodes of both a plain (-r) and a COW (-R) disk, runs as PID1 via -e init_path=/poc (no userland needed; mounts devfs itself), and logs to unbuffered stderr because the vkernel userland console is unreliable with stdin=/dev/null. vkernel64 also needed a guest-side build workaround (defsym __build_id_start/end) because HEADs vkernel link is broken.", "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": 2100, "guest_dirty": 1, "build_cmd": "config VKERNEL64 && make depend && make -j6 in /usr/src/sys/compile/VKERNEL64; cc -static -O2 -o poc vkd_bounds.c; vn/newfs root.img (see build.sh)", "run_cmd": "timeout 90 ./vkernel64 -m 256M -r root.img -r plain.img -R cow.img -e init_path=/poc < /dev/null (see run.sh)", "code_hash": "66ec5fadfdba78e2ab87b1653f8d781bc267ad3b38b169c49e4fe77fafbb2499", "notes": "b_resid underflow (n=-1 -> resid=bcount+1 -> physio iolen=(size_t)-1) also investigated and killed: std_copyouts address-wrap guard turns it into an immediate EFAULT with zero bytes copied. vkernel64 is currently un-buildable in HEAD (SYSTEM_LD override drops the ldscript that defines __build_id_start/end referenced by kern_mib.c:102-103) - upstream should fix separately.", "recommended_fix": "Add driver-level bounds check in vkd_doio() rejecting bio_offset<0 or bio_offset+b_bcount>sc->size with EINVAL, and clamp n<0 to 0 before computing b_resid (see fix.diff)", "fix_status": "fixed", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff applied to guest /usr/src (checksum-identical source), vkernel rebuilt, same PoC rerun: identical clean behaviour (COW disk armed, PoC completed, vkernel survived, backing file unchanged) - no regression, driver-level rejection now armed as defense-in-depth. Baseline never reproduced the OOB (dscheck shield), so patched-vs-baseline shows no behavioural delta by design.", "fix_evidence": "build-fix.log (rebuild with patched vdisk.c), run.4-fixed.log (patched run: vkd2: COW disk, init died (signal 0, exit 0), plain.img md5 unchanged)", "status": "not_reproduced", "reproduced": false } |