DF-5677 / df5677_emergency.c
/* * DF-5677 โ prop_object_release_emergency() NULL-parent dispatch. * * prop_object.c:1005-1052: if the FIRST loop iteration breaks via * `ocnt != 1` (someone else holds a reference โ the exact situation the * emergency path exists for), `parent` is still NULL, and the function * ends with * * po = parent; * (*po->po_type->pot_emergency_free)(parent); <-- NULL deref * * (in a non-INVARIANTS kernel the _PROP_ASSERT(parent) at :1048 is a * no-op; in userland assert() aborts first). * * The emergency path is reached from prop_object_release only when * pot_free returns _PROP_OBJECT_FREE_FAILED, which for array/dict happens * only when _prop_stack_push fails โ i.e. malloc failure (userland OOM; * in-kernel kmalloc(M_WAITOK) cannot fail, it panics first), or via a * concurrent retain racing the restored refcount. This harness calls the * static function directly (by including the translation unit) with a * refcount-2 object to demonstrate the defect deterministically. */ #include "prop_object.c" #include <stdio.h> #include "prop_array.h" int main(void) { prop_array_t a; a = prop_array_create(); /* po_refcnt = 1 */ prop_object_retain(a); /* po_refcnt = 2 (racing holder) */ printf("DF5677: calling prop_object_release_emergency on " "refcnt=2 array (first iteration must break, parent==NULL)\n"); fflush(stdout); /* userland _PROP_ASSERT(parent) fires here; without assert this is a * NULL deref at offsetof(_prop_object_type, pot_emergency_free) */ prop_object_release_emergency(a); printf("DF5677: survived (bug not present)\n"); return (0); } |