/*
 * DF-5678 — kernel trigger module (loaded as root on the audit guest).
 *
 * Exercises the exact code path in the running stock kernel
 * (X86_64_GENERIC, "options INVARIANTS" -> _PROP_ASSERT == KKASSERT
 * active):
 *
 *   df5678_set.ko : prop_array_set(pa, idx > count)  -> expect
 *                   panic: assertion "idx < pa->pa_count" failed
 *                   in prop_array_set at prop_array.c:648
 *   df5678_rem.ko : prop_array_remove(pa, idx >= count) -> expect
 *                   panic ... in prop_array_remove at prop_array.c:701
 *
 * On a kernel built WITHOUT INVARIANTS the same calls are silent: the
 * assert is compiled to nothing and :650/:654/:709/:719 execute OOB (see
 * the userland -DNDEBUG differential for that half).
 *
 * On a kernel with the fix (real bounds checks) both modules load cleanly
 * and print their observations — that is the fix validation.
 *
 * Build:  make -DMODE_SET   -> df5678_set.ko
 *         make -DMODE_REM   -> df5678_rem.ko
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <libprop/proplib.h>

#if !defined(MODE_SET) && !defined(MODE_REM)
#define	MODE_SET	1	/* default */
#endif

static int
df5678_modevent(module_t mod __unused, int type, void *arg __unused)
{
	prop_array_t pa;
	prop_object_t po;
#if defined(MODE_SET)
	int rv;
#endif

	switch (type) {
	case MOD_LOAD:
		pa = prop_array_create_with_capacity(2);
		po = prop_string_create_cstring("x");
		if (pa == NULL || po == NULL)
			return (ENOMEM);
		/* count == capacity == 2: backing store is exactly 16 bytes */
		if (prop_array_add(pa, po) == false ||
		    prop_array_add(pa, po) == false) {
			kprintf("DF5678: setup add failed\n");
			return (ENOMEM);
		}
		kprintf("DF5678: array ready count=%u cap=%u\n",
		    prop_array_count(pa), prop_array_capacity(pa));
#if defined(MODE_SET)
		kprintf("DF5678: calling prop_array_set(pa, 5, po)\n");
		rv = prop_array_set(pa, 5, po);
		/* INVARIANTS kernel: never returns (panic at prop_array.c:648).
		 * Fixed kernel: returns 0 (false). */
		kprintf("DF5678: set(5) returned %d, count=%u\n",
		    rv, prop_array_count(pa));
#else	/* MODE_REM */
		kprintf("DF5678: calling prop_array_remove(pa, 7)\n");
		prop_array_remove(pa, 7);
		/* INVARIANTS kernel: panic at prop_array.c:701.
		 * Fixed kernel: no-op, count unchanged. */
		kprintf("DF5678: remove(7) returned, count=%u\n",
		    prop_array_count(pa));
#endif
		prop_object_release(po);
		prop_object_release(pa);
		kprintf("DF5678: module completed without panic\n");
		return (0);
	default:
		return (EOPNOTSUPP);
	}
}

static moduledata_t df5678_mod = {
#if defined(MODE_SET)
	"df5678",
#else
	"df5678rem",
#endif
	df5678_modevent,
	NULL
};

DECLARE_MODULE(df5678, df5678_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
