DF-4536 / verdict.json
{ "finding_id": "DF-4536", "status": "not_reproduced", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "The check-then-set of sc_state in lptopen() (lpt.c:458-462) is unsynchronized in the driver and, if ever executed concurrently, double-opens the unit: two kmalloc pairs (first leaks) and two closes that kfree the same second pair (heap double-free in the M_LPT zone). On the audited tree it cannot manifest: lpts dev_ops flags are 0 (lpt.c:191-198), so dev_needmplock() is true and dev_dopen() executes d_open under the kernel mplock (sys/kern/kern_device.c:119,142) - concurrent lptopen() entries are impossible. Verified empirically: 0 double-opens in 300 same-vnode full-path rounds, 300 cross-cdev rounds, and ~3,600 cross-devfs-mount rounds (a configuration that removes every other serializer, isolating the mplock). An initial calibration run that printed many double-successes was proven to be sequential pairs (sibling opening after the holders close) and the PoC was hardened with an fd-hold to eliminate the false signal. Filed at Info as a latent defect: it becomes a live double-free the day lpt is converted to D_MPSAFE without adding per-unit serialization (fix.diff adds a spinlock now).", "exploit_chain": "", "evidence": "run.log (all three race configurations + the calibration false-positive analysis); VERDICT.md (the mplock argument with path:line); fix.diff (git-apply-clean spinlock serialization)", "kernel_refs": [ "sys/dev/misc/lpt/lpt.c:458", "sys/dev/misc/lpt/lpt.c:539", "sys/dev/misc/lpt/lpt.c:598", "sys/dev/misc/lpt/lpt.c:191", "sys/kern/kern_device.c:119", "sys/kern/kern_device.c:142", "sys/vfs/devfs/devfs_vnops.c:devfs_spec_open" ], "poc_changes": "PoC written for this finding (no seed). Two hardening iterations during verification: (1) per-child paths so different cdevs/vnodes/devfs-mounts can be raced; (2) fd-hold before close so sequential open/close pairs cannot produce false positives - the initial .ctl-vs-.ctl hits were shown to be sequential.", "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": 480, "guest_dirty": 0, "build_cmd": "cc -O -Wall -o lpt_openrace lpt_openrace.c", "run_cmd": "./lpt_openrace /dev/lpt0 /dev/lpt0 300 350 ; ./lpt_openrace /dev/lpt0 /dev/lpt0.ctl 300 450 ; ./lpt_openrace /dev/lpt0.ctl /mnt/df2/lpt0.ctl 30000 3 25000", "code_hash": "29d69127d5ddcc3c849af61ae805663685e14ce99c37a26ea9650180c11f7208", "notes": "Severity Info (latent): the driver-level TOCTOU is real but incidentally serialized by the mplock today; converting lpt to D_MPSAFE without the fix turns it into a reachable heap double-free. Device node is 0600 root:wheel regardless.", "recommended_fix": "Serialize the open state transition and close-time frees with a per-unit spinlock in struct lpt_data (fix.diff) - required before any D_MPSAFE conversion.", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "Fix validation not applicable: the defect does not manifest on the current (non-MPSAFE) configuration, so there is no baseline behavior to compare. The spinlock fix is defensive for a future D_MPSAFE conversion.", "fix_evidence": "fix.diff (git apply --check clean against sys/)" } |