DF-3160 / fifo_open_race.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | /* * DF-3160 PoC attempt: lost-wakeup race in fifo_open()'s reader/writer * rendezvous (sys/vfs/fifofs/fifo_vnops.c:295-335). * * The writer's path: check `fi_readers == 0` under fifo_lock, RELEASE * fifo_lock, RELEASE vn lock, tsleep("fifoow"). A reader arriving in the * gap between the unlock and the tsleep-enqueue sends wakeup(&fi_writers) * to nobody - lost. The writer then sleeps until the NEXT readers 0->1 * transition, i.e. stuck for however long the reader keeps its fd open. * * Writer (this mode "w"): blocking open(O_WRONLY) in a loop, measure * elapsed. Normal return: microseconds (a reader is open ~100% of the * time; the writer can only even *enter* tsleep during the reader's * close/reopen gap, and is then rescued by the very next 0->1 wakeup * unless the wakeup is lost). elapsed >> 1ms == lost wakeup. * Reader (mode "r"): open(O_RDONLY|O_NONBLOCK)/close at max rate, * holding the fd 1000ms on every 8th cycle (to let a lost wakeup * become visible as a slow writer open). * * usage: ./fifo_open_race r <path> | ./fifo_open_race w <path> [seconds] */ #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <fcntl.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> static double now(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (ts.tv_sec + ts.tv_nsec / 1e9); } int main(int argc, char **argv) { const char *path; int fd, i, hits = 0, iters = 0; double t0, el, maxel = 0; int runtime = argc > 3 ? atoi(argv[3]) : 60; if (argc < 3) { fprintf(stderr, "usage: %s r|w <fifo> [seconds]\n", argv[0]); exit(2); } path = argv[2]; if (argv[1][0] == 'r') { int holdms = argc > 3 ? atoi(argv[3]) : 1000; int every = argc > 4 ? atoi(argv[4]) : 8; i = 0; for (;;) { fd = open(path, O_RDONLY | O_NONBLOCK); if (fd < 0) { if (errno == ENXIO) continue; perror("reader open"); exit(1); } if (++i % every == 0) usleep(holdms * 1000); /* hold fd open */ close(fd); } /* not reached */ } /* writer */ signal(SIGPIPE, SIG_IGN); t0 = now(); while (now() - t0 < runtime) { double s = now(); fd = open(path, O_WRONLY); el = now() - s; if (fd < 0) { if (errno == EINTR) continue; perror("writer open"); exit(1); } close(fd); ++iters; if (el > maxel) maxel = el; if (el > 0.050) { printf("LOST-WAKEUP-SUSPECT: open took %.1f ms " "(iter %d)\n", el * 1000, iters); fflush(stdout); ++hits; } } printf("writer done: %d opens, %d slow (>50ms), max %.1f ms\n", iters, hits, maxel * 1000); return (hits ? 0 : 1); } |