DF-2816 / sibs.c
/* * DF-2816 - boot()/shutdown_cleanup_proc() vs sibling threads. * * sys_reboot() -> boot() calls shutdown_cleanup_proc(curproc) * (sys/kern/kern_shutdown.c:291) while the calling process's OTHER * threads keep running on other CPUs. shutdown_cleanup_proc() * - kern_closefrom(0) closes every fd, * - cache_drop(&fdp->fd_ncdir) NULLs fd_ncdir.ncp (vfs_cache.c:1090) * while leaving fd_ncdir in place, * - vm_map_remove()s the whole user address space (:601-609). * * A sibling thread that then performs any path lookup copies the NULL-ed * nchandle into nd->nl_nch (vfs_nlookup.c:159-160 cache_copy has no NULL * guard) and naccess() dereferences nch->ncp at +0x58 * (vfs_nlookup.c:658) -> kernel NULL-deref page fault -> panic. * * Siblings here just retry open("etc/passwd") in a loop (relative path * -> fd_ncdir anchor). No dump-device churn involved at all. */ #include <sys/reboot.h> #include <sys/syscall.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> static volatile int stop; static void * sib(void *x __unused) { int fd; while (!stop) { fd = open("etc/passwd", O_RDONLY); /* relative -> cwd */ if (fd >= 0) close(fd); } return (NULL); } int main(int argc, char **argv) { pthread_t tid[64]; int n = 4; int i; if (argc > 1) n = atoi(argv[1]); if (n > 64) n = 64; for (i = 0; i < n; i++) pthread_create(&tid[i], NULL, sib, NULL); sleep(1); printf("sibs: calling reboot(RB_DUMP|RB_NOSYNC) with %d live siblings\n", n); fflush(stdout); syscall(SYS_reboot, RB_AUTOBOOT | RB_DUMP | RB_NOSYNC); printf("sibs: returned?!\n"); return (0); } |