DF-2863 / verdict.json
{ "finding_id": "DF-2863", "status": "reproduced", "reproduced": 1, "impact": "dos", "confidence": "certain", "verdict": "vfs_mountroot_try() computes the ';' segment end (ep, vfs_conf.c:423) but strncpy(mf, cp, MFSNAMELEN+MNAMELEN) at :427 ignores it, copying the whole remaining candidate list into the 96-byte parse buffer; the %80s conversion terminates devname only on whitespace, so every non-final root-mount candidate gets a device name contaminated with the ';<later candidates>' tail and fails device lookup. The fallback list silently degenerates to 'last candidate only': a valid earlier candidate can never mount (boot panic if the last is absent, vfs_conf.c:208) or a different-than-intended device gets mounted as root. Proven with the kernel's own ksscanf in a KLD replica (devname='da0s1a;ufs:da1s1a' for candidate 1) and on the real boot path (stock kernel #0, crafted vfs.root.mountfrom: 'no disk named da0s1a;ufs:' printed at boot, candidate never attempted). Fixed on rebuilt kernel #1: 'no disk named da0s1a' โ clean segment parse, same input.", "exploit_chain": "no escalation chain: boot-time configuration handling (loader.conf / mountroot console). Worst cases: boot-time DoS via valid-candidate-skipped -> panic('Root mount failed'), or wrong-device-as-root selection", "evidence": [ "run.log: A1 'cand 1: vfsname=ufs devname=da0s1a;ufs:da1s1a (len=17)' vs A2 FIXED 'devname=da0s1a (len=6)'", "bootpath-baseline.log: stock kernel #0 boot โ 'no disk named da0s1a;ufs:' then candidate skipped", "bootpath-patched.log: fixed kernel #1, identical loader.conf โ 'no disk named da0s1a' (contamination gone)" ], "kernel_refs": [ "sys/kern/vfs_conf.c:422", "sys/kern/vfs_conf.c:423", "sys/kern/vfs_conf.c:427", "sys/kern/vfs_conf.c:430", "sys/kern/vfs_conf.c:431", "sys/kern/vfs_conf.c:435", "sys/kern/vfs_conf.c:447", "sys/kern/subr_scanf.c:359", "sys/kern/vfs_conf.c:208" ], "poc_changes": "authored fresh (no seed): KLD replica embedding a verbatim copy of vfs_conf.c:419-431 plus a fixed variant, driven through the kernel's own ksscanf/kmalloc; boot-path run via /boot/loader.conf vfs.root.mountfrom", "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": 1800, "guest_dirty": 0, "build_cmd": "cd /root/dfrep && make (fix validation: cd /usr/src && make -j4 quickkernel KERNCONF=X86_64_GENERIC && make installkernel KERNCONF=X86_64_GENERIC)", "run_cmd": "kldload /root/dfrep/dfrep.ko && dmesg | tail -30 (boot-path: test vfs.root.mountfrom in /boot/loader.conf + reboot)", "code_hash": "cd7d001f8c11665b10f68b6a5fd0a4acf33d2816b2a20ec91adadfaf7a5753ca", "notes": "shares its fix hunk and evidence module with DF-2862 (same strncpy at vfs_conf.c:427); the two findings are distinct defects of the same statement: DF-2863 is the ignored ep bound (wrong parse result), DF-2862 is the missing NUL termination (OOB read).", "recommended_fix": "bound the copy at the ';' segment: seglen = min(ep - cp, MFSNAMELEN+MNAMELEN - 1); bcopy(cp, mf, seglen) (see fix.diff, shared with DF-2862)", "fix_status": "fixed", "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Wed Sep 2 13:46:57 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "kernel #1 with fix.diff, identical test loader.conf: candidate 1's device name is exactly 'da0s1a' ('no disk named da0s1a') instead of 'da0s1a;ufs:' โ contamination eliminated; boot completes via the last candidate as configured; replica A2 confirms per-candidate parsing", "fix_evidence": [ "bootpath-baseline.log (kernel #0) vs bootpath-patched.log (kernel #1)", "run.log A1 (BUGGY) vs A2 (FIXED)" ] } |