DF-5063 / verdict.json
{ "finding_id": "DF-5063", "status": "not_reproduced", "reproduced": false, "impact": "none", "confidence": "certain", "verdict": "Plexno()/Sdno() (sys/dev/raid/vinum/vinumutil.c:261,289) decode composite VINUM_PLEX_TYPE/VINUM_SD_TYPE minors with no bounds checks and propagate the -1 stored in empty vol->plex[] slots: Sdno() computes PLEX[-1].sdnos[ordinal], a heap OOB read whose value then indexes SD[]; callers only check >= so -1 survives (vinum.c:430-440). The defect is certain by inspection, but the paths are UNREACHABLE on DragonFly: no make_dev site creates composite minors (only raw VINUM_SD/VINUM_PLEX and volume nodes, vinumconfig.c:740/763/780), and hand-mknoded char nodes never reach vinumopen -- reading one, even with a VALID vinum rawplex minor (0x50000000), panics earlier in the generic UFS->spec/devfs layer (Fatal trap 12, dev_dflags(NULL), rdi=0, fault va 0xa8, proc dd; reproduced twice, panic.txt) because nothing resolves v_rdev for mknoded VCHR vnodes. Downgraded to Info hardening; the identical PLEX[-1] sink remains reachable via the config path (DF-1399). Fix (bounds checks in Plexno/Sdno, shared fix.diff) applied and regression-validated on a rebuilt vinum.ko.", "exploit_chain": "", "evidence": "panic.txt (Fatal trap 12 dev_dflags NULL after reading an mknoded node with a VALID minor -- composite minors die the same way before vinum); run.log (no mknod(1) on guest; device-node minors show raw-only: sd 0x60000000, plex 0x50000000); fix_run2.log (patched module regression: raw sd/plex/vol I/O intact)", "kernel_refs": [ "sys/dev/raid/vinum/vinumutil.c:250-270", "sys/dev/raid/vinum/vinumutil.c:277-297", "sys/dev/raid/vinum/vinumconfig.c:896-897", "sys/dev/raid/vinum/vinum.c:429-440", "sys/kern/vfs_subr.c:1246-1252" ], "poc_changes": "wrote df5063_mknod.c (guest lacks mknod(1)); first version had an argc==5 bug for the 3-arg create form (fixed to argc==4); composite sweep never reached vinum because reading ANY hand-made char node panics the generic device layer first", "attempts": 3, "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": 240, "guest_dirty": 0, "build_cmd": "cc -O -o /tmp/mk /tmp/df5063_mknod.c", "run_cmd": "sh /tmp/df5063_setup.sh; /tmp/mk /tmp/px0 32 1342177280; dd if=/tmp/px0 bs=512 count=1 (panics upstream of vinum)", "code_hash": "839215ff81f5ee474b38312595686bf35eaa886309b9daefe65b900c0aba24d8", "notes": "Cross-file discovery for the orchestrator: reading ANY mknoded character device node on this DragonFly panics the kernel (dev_dflags NULL-cdev deref; root-gated local DoS; lives in the UFS->spec/devfs resolution chain, NOT vinum). Evidence in panic.txt. The DF-5063 bounds-check fix rides in the same vinum fix.diff as DF-5062 and was compile+regression validated there.", "recommended_fix": "Plexno()/Sdno(): bounds-check volno/plexno against volumes_allocated/plexes_allocated and return -1 instead of indexing VOL[]/PLEX[] with -1 (see fix.diff)", "fix_status": "fixed", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "Fix cannot be exercised at runtime (the composite path is unreachable before and after -- no behavior change observable by design). Validated by clean rebuild of vinum.ko with the extra checks (fix_build.log via DF-5062 pack) plus a regression run: raw sd/plex/volume reads and volume write+readback all correct on the patched module (fix_run.log, fix_run2.log).", "fix_evidence": "findings/poc/DF-5063/fix_run2.log (SD-S0-DATA via /dev/vinum/sd/s0, /dev/vinum/plex/p0 and /dev/vinum/vol/v0; PATCHED-OK volume write/readback); fix.diff (shared with DF-5062)" } |