⬢ DragonFlyBSD Kernel Audit
DF-5010 / vkd_bounds.c
← back to finding ↓ download raw
/*
 * DF-5010 — vkernel vdisk (vkd) bounds-check verification trigger.
 *
 * Runs as PID 1 inside the vkernel guest:
 *     ./vkernel64 -m 256M -r root.img -r plain.img -R cow.img \
 *                 -e init_path=/poc
 *   /dev/vkd1 = plain.img (non-COW, 16 MB)
 *   /dev/vkd2 = cow.img   (COW,     8 MB)
 *
 * All I/O 512-byte aligned so the dscheck() sector-alignment rejection
 * (sys/kern/subr_diskslice.c:131-143) does not mask the bounds behaviour.
 * Expected on a stock kernel:
 *   in-bounds 512B write/read        -> succeeds
 *   OOB write/read @1TB, @size+512   -> -1 EINVAL (dscheck subr_diskslice.c:253)
 *   at-exact-end write               -> 0 (EOF semantics)
 *   the vkernel process SURVIVES everything
 * (init exits afterwards -> "panic: Going nowhere without my init" is
 *  expected and harmless.)
 */

#include <sys/mount.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

static char buf512[512];
static int logfd = 2;		/* /marker once opened */

static void
logmark(const char *fmt, ...)
{
	char buf[160];
	va_list ap;
	int n;

	va_start(ap, fmt);
	n = vsnprintf(buf, sizeof(buf), fmt, ap);
	va_end(ap);
	if (n > 0) {
		write(logfd, buf, n);
		write(2, buf, n);
	}
}

static void
tryop(const char *what, int fd, int wr, off_t off)
{
	ssize_t n;
	int save;

	if (wr)
		n = pwrite(fd, buf512, 512, off);
	else
		n = pread(fd, buf512, 512, off);
	save = (n < 0) ? errno : 0;
	logmark("[*] %-28s @%#11jx -> %zd%s%s\n", what, (intmax_t)off, n,
	    (n < 0) ? " errno=" : "", (n < 0) ? strerror(save) : "");
}

int
main(void)
{
	int fd;

	memset(buf512, 0x5a, sizeof(buf512));

	/* markers also go to a file on the root disk (console may be dead) */
	logfd = open("/marker", O_WRONLY | O_CREAT | O_TRUNC, 0644);

	mkdir("/dev", 0755);
	if (mount("devfs", "/dev", 0, NULL) < 0)
		logmark("[-] mount devfs failed errno=%d\n", errno);
	else
		logmark("[+] devfs mounted\n");

	fd = open("/dev/vkd1", O_RDWR);
	if (fd < 0) {
		logmark("[-] open /dev/vkd1 failed errno=%d\n", errno);
		_exit(1);
	}
	logmark("[+] vkd1 (non-COW 16MB) open\n");
	tryop("vkd1 write in-bounds", fd, 1, 0);
	tryop("vkd1 write @exact-EOF", fd, 1, 16 * 1024 * 1024);
	tryop("vkd1 write @1TB", fd, 1, 1LL << 40);
	close(fd);

	fd = open("/dev/vkd2", O_RDWR);
	if (fd < 0) {
		logmark("[-] open /dev/vkd2 failed errno=%d\n", errno);
		_exit(1);
	}
	logmark("[+] vkd2 (COW 8MB, map_buf) open\n");
	tryop("vkd2 write in-bounds", fd, 1, 0);
	tryop("vkd2 write @size+512", fd, 1, 8 * 1024 * 1024 + 512);
	tryop("vkd2 write @1TB", fd, 1, 1LL << 40);
	tryop("vkd2 read  @1TB", fd, 0, 1LL << 40);
	close(fd);

	logmark("[+] vkernel survived all OOB attempts "
	    "(dscheck bounds enforcement held)\n");
	_exit(0);
}