DF-2941 / verdict.json
{ "finding_id": "DF-2941", "status": "reproduced", "reproduced": 1, "impact": "none", "confidence": "certain", "verdict": "Mechanism reproduced on the stock INVARIANTS guest via a KLD probe with a synthetic sysref_class. _sysref_put's `count > -0x40000000` branch (kern_sysref.c:329-336) accepts count==0: a double-release walked a fully-released object 0 -> -1 with no floor guard, after fault-injecting the exact state (SRF_PUTAWAY not yet visible) that a racing put observes between the refcnt cmpset at :346 and the flags RMW at :348 -- accepted even on the INVARIANTS kernel (run.log 'B2: layer ACCEPTED double-release: refcnt=-1'). A real post-putaway double-put then panicked at the layer's only guard, the debug-only KKASSERT at kern_sysref.c:303 (panic.txt: 'assertion \"(sr->flags & SRF_PUTAWAY) == 0\" failed in _sysref_put'), proving the barrier is debug-only; production builds compile it out and take the B2 path silently. Attacker reachability: none found in-tree -- triggering requires a kernel-side double-release caller bug (none exists among sysref users); the finding is that the layer converts any such future bug into a silent refcount underflow instead of detecting it (Info hardening, same family as the get-side DF-0169).", "exploit_chain": "", "evidence": "findings/poc/DF-2941/run.log: 'B2: layer ACCEPTED double-release: refcnt=-1 (walked 0 -> -1; `count > -0x40000000` branch, no floor guard)' -- silent acceptance on the INVARIANTS kernel.\nfindings/poc/DF-2941/panic.txt: panic at kern_sysref.c:303 from the un-injected post-putaway double-put (backtrace _sysref_put <- sysref_probe_modevent <- linker_load_module) -- the only guard is debug-only.\nfindings/poc/DF-2941/run.log: 'B1: normal cycle refcnt=0 flags=0006 term_count=1 (putaway)' -- baseline protocol cycle.\nfindings/poc/DF-2941/build.log: clean KLD build (cc 8.3, INVARIANTS kernel #0).\nsys/kern/kern_sysref.c:329-336 accepting branch; :345-355 final release with the :346->:348 non-atomic flags window; :163 recycle-time KKASSERT.", "kernel_refs": [ "sys/kern/kern_sysref.c:305", "sys/kern/kern_sysref.c:329", "sys/kern/kern_sysref.c:346", "sys/kern/kern_sysref.c:348", "sys/kern/kern_sysref.c:303", "sys/kern/kern_sysref.c:163", "sys/sys/sysref2.h:85" ], "poc_changes": "Authored from scratch (no seed): synthetic sysref_class KLD mirroring devfs_cdev_terminate's lock/unlock/terminate protocol; trigger B2 uses fault injection (clear SRF_PUTAWAY) to emulate the pre-:348 visibility a racing double-put sees, since the genuine window is ~instruction-width; repaired the injected state afterwards so B3 could run in the same guest boot.", "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": 420, "guest_dirty": 0, "build_cmd": "ssh -F dfbsd-qemu/config dfbsd 'cd /root/sysref_probe && make' (after scp of sysref_probe.c + Makefile)", "run_cmd": "ssh -F dfbsd-qemu/config dfbsd 'kldload /root/sysref_probe/sysref_probe.ko'", "code_hash": "8e3ef23300267dbc68db2f94e6e462d035b87f6a8c76f1343ae27194d0ea81a7", "notes": "Guest reset (with-src) after the run; guest_dirty=0. Shared probe module also drives DF-2942 trigger A (same run.log). The underflow is self-limiting in practice: reaching a second objcache_put of the same object needs ~2^30 further puts, so the realistic ceiling is silent loss of detection, not an immediate double-free. Known findings not re-reported: DF-0168 (lazy objcache creation race), DF-0169 (get-side overflow guard absence -- same numeric-guard family as this put-side gap).", "recommended_fix": "Add an explicit count==0 terminal branch to _sysref_put that refuses to decrement (KKASSERT loudly on INVARIANTS builds) instead of letting double-releases silently underflow through the `count > -0x40000000` branch.", "fix_status": "not_testable", "fix_verdict": "Fix authored post-verification (fix.diff); not build-validated: Info hardening finding, the observable behavior change of the fix is the panic/no-op itself, and no memory corruption was reproduced to A/B test against." } |