DF-5681 / verdict.json
{ "finding_id": "DF-5681", "status": "not_reproduced", "reproduced": false, "impact": "none", "confidence": "likely", "verdict": "Kernel-only lazy-init race: _PROP_ONCE_RUN compiled for the kernel (prop_object_impl.h:302-303) is a cmpset-once with no completion barrier, so the first-ever concurrent prop_number use lets the cmpset loser reach lockmgr()/_prop_rb_tree_find() while the winner is still inside _prop_number_init() (prop_number.c:169-176) - lockinit overwrites the losers lock state (kern_lock.c:1300-1307) and rbt_ops may still be NULL (prop_rb.c:90-93 NULL indirect call -> panic). Userland is safe (pthread_once). The guest kernel itself was NOT crashed (the sub-microsecond first-use window closed during boot), hence not_reproduced; the rig proves the mechanism deterministically: stock userland libprop survives 8-thread barrier first-use, the same sources with the kernel cmpset-once port SIGSEGV 5/5 in _prop_rb_tree_find, and pre-completing the once (the SYSINIT fix) survives 5/5. Realistic kernel worst case is a boot-time-flavored panic; broken-mutex rb-tree corruption is theoretical. Fix: bootstrap the once at SI_SUB_PROP (same point the number objcache pool is created), validated at rig level.", "exploit_chain": "", "evidence": "run.log lines: control exit=0 / --- ksem run1..5 exit=139 (SIGSEGV with kernel once-macro semantics) / --- fix run1..5 exit=0; VERDICT.md traces the macro at prop_object_impl.h:302-303 vs :428-430 and the crash path prop_number.c:277->283->284 + prop_rb.c:90-93; fix.diff = SI_SUB_PROP SYSINIT bootstrap.", "kernel_refs": [ "sys/libprop/prop_number.c:167", "sys/libprop/prop_number.c:169-190", "sys/libprop/prop_number.c:272-320", "sys/libprop/prop_object_impl.h:302-303", "sys/libprop/prop_object_impl.h:428-430", "sys/libprop/prop_rb.c:90-93", "sys/kern/kern_lock.c:1300-1307", "sys/libprop/prop_object.c:1077-1097" ], "poc_changes": "Authored fresh (no seed): race_once.c harness; ksem_object_impl.h (userland once swapped for a faithful port of the kernel cmpset-once) and ksem_number.c (50ms window-widening usleep inside _prop_number_init, instrumentation only) generated from the md5-identical guest sources.", "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": 1, "guest_dirty": 0, "build_cmd": "sh /tmp/build5681.sh (cc -O2 -fPIC -I. -I/usr/src/sys each .c of two libprop copies from /usr/src/sys/libprop; link race_once, race_once_ksem)", "run_cmd": "sh /tmp/run5681.sh", "code_hash": "f6a0e2d561aca45ff4798aea9b55ca52d9bcdca685f077403b081f1eabc79032", "notes": "Status honest per contract: the finding targets the kernel build; the guest kernel window (first-ever use, sub-us) closed at boot and no runtime trigger can re-open it, so kernel-binary repro is not achievable -> not_reproduced despite the deterministic rig crash (ksem variant, same sources, kernel macro semantics, 5/5 SIGSEGV). DF-5674 twin-check for this file is CLEAN: prop_number registers pot_lock/pot_unlock (prop_number.c:84-85) and prop_object_release/prop_object_release_emergency hold them across pot_free, and _prop_number_allocs drop-and-recheck (283-318) is correctly serialized. Same racy once-macro also used by prop_dictionary.c:288,401 and prop_bool.c:131 (pattern generalizes; fix is per-file here, macro-level upstream).", "recommended_fix": "Bootstrap the lazy init single-threaded at SI_SUB_PROP via SYSINIT so the cmpset-once can never race first use (see fix.diff); alternatively give the kernel _PROP_ONCE_RUN real completion semantics.", "fix_status": "fixed", "fix_kernel_uname": null, "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Rig-level fix validation: baseline (kernel once-macro semantics) SIGSEGV 5/5; fix emulation (once completed before first concurrent use = exactly the SYSINIT bootstrap of fix.diff) survives 5/5 with interning invariant intact. Single-fix kernel rebuild not performed: no kernel memory-corruption reproduction gates it, and the fixs ordering guarantee (consumers run after SI_SUB_PROP) is the same in-tree guarantee the existing _PROP_POOL_INIT SYSINIT already relies on.", "fix_evidence": "run.log section 3 (fix runs 1-5 exit=0, SURVIVED: interning invariant held) vs section 2 (ksem runs 1-5 exit=139); fix.diff adds the SI_SUB_PROP SYSINIT bootstrap to sys/libprop/prop_number.c." } |