โฌข DragonFlyBSD Kernel Audit
DF-5677 / df5677_emergency.c
โ† back to finding โ†“ download raw
/*
 * 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);
}