# DF-2941 fix: put-side floor guard in _sysref_put # # A double-release of a fully-released sysref object currently falls into # the `count > -0x40000000` branch (kern_sysref.c:329) and is silently # accepted as a 0 -> -1 underflow. The only existing barrier is the # debug-only KKASSERT at kern_sysref.c:303, whose flags read races the # refcnt-cmpset (:346) -> SRF_PUTAWAY-RMW (:348) pair in the final-release # branch, so even INVARIANTS kernels accept a put that lands in that window # (demonstrated: findings/poc/DF-2941/run.log, "B2: layer ACCEPTED # double-release: refcnt=-1"). # # This diff makes count==0 an explicit, loud terminal case: on INVARIANTS # kernels the misuse panics at the point of the bug instead of corrupting # the count; on production kernels the put is a no-op instead of an # underflow. # # Authored after verification; NOT applied to the read-only sys/ tree. --- a/sys/kern/kern_sysref.c +++ b/sys/kern/kern_sysref.c @@ -326,6 +326,16 @@ _sysref_put(struct sysref *sr) sr->srclass->ops.unlock(data); } else if (count > -0x40000000) { /* + * DF-2941: count == 0 must never reach the generic + * decrement below. A put on a fully-released object + * is a double-release (caller bug): fail loudly on + * INVARIANTS kernels, no-op on production builds. + */ + if (count == 0) { + KKASSERT(count != 0); + break; + } + /* * release 1 count, nominal case, resource undergoing * termination. The Resource can be ref'd and * deref'd while undergoing termination.