DF-5808 / verdict.json
{ "finding_id": "DF-5808", "status": "reproduced", "reproduced": true, "impact": "leak", "confidence": "certain", "verdict": "hammer_vop_mountctl() double-counts the MOUNTCTL_MOUNTFLAGS result byte count (*ap->a_res already holds part1, then *ap->a_res += part1+part2), and sys_mountctl() copyout()s the VOP-reported length from the kmalloc(buflen+1) result buffer with no clamp; MOUNTCTL_MOUNTFLAGS is exempt from the root check, so any local user over-reads part1-2 bytes of adjacent kernel heap per call (observed ret=170 from an 87-byte allocation, 3/3 deterministic; tmpfs control clean). Fixed kernel returns ret == strlen.", "exploit_chain": "unprivileged mountctl(mount-root-path, MOUNTCTL_MOUNTFLAGS, buflen=strlen(flags)+1) -> kernel returns 2*part1 -> sys_mountctl copyout(buf, user, 2*part1) from buflen+1 heap buffer -> repeatable OOB kernel heap disclosure sized by the mounts flag string", "evidence": "run.log (3x ret=170 buflen=86 + tmpfs control ret=5); run_fixed.log (ret=85 after fix); mctl2.c; fix.diff", "kernel_refs": [ "sys/vfs/hammer/hammer_vnops.c:2559-2582", "sys/kern/vfs_syscalls.c:1281-1284", "sys/kern/vfs_syscalls.c:1317-1332", "sys/kern/vfs_default.c:1310-1317" ], "poc_changes": "trigger written from scratch (mctl.c/mctl2.c); hammer1 test mount created on guest (vn+newfs_hammer -f); NULL-buflen variant of the same probe is filed separately as DF-5811", "attempts": 3, "guest_uname": "DragonFly dfbsd 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": 900, "guest_dirty": 0, "build_cmd": "cc -O2 -o /tmp/mctl2 mctl2.c", "run_cmd": "(root) mount -t hammer -o ro,noexec,nosuid,nodev,nosymfollow,noatime,noclusterr,noclusterw /dev/vn1 /mnt/h; (user uid1001) /tmp/mctl2 /mnt/h 86 3", "code_hash": "sha256 of mctl2.c recorded in SHA256SUMS", "notes": "adjacent heap was zero-filled in this guest state; over-read extent and determinism are structural (copyout length 2x the allocation). On hammer1-root production hosts (DF <=5.x default) this is default-config unpriv leak; hammer2 unaffected (no mountctl override).", "recommended_fix": "hammer_vnops.c: *ap->a_res = usedbytes (not +=); plus clamp sysmsg_result to uap->buflen in sys_mountctl before copyout (both in fix.diff, validated)", "fix_status": "fixed", "fix_baseline_reproduced": true, "fix_patched_reproduced": false, "fix_verdict": "baseline: ret=170 from 87-byte buffer 3/3; after fix.diff + kernel rebuild + reboot: ret=85 == strlen on all runs, tmpfs and hammer both clamped (run_fixed.log)", "fix_evidence": "run_fixed.log; fix.diff" } |