DF-2568 / race_flush_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 | /* * DF-2568 — aggressive hammer2 flush NULL-deref race (v3: population churn). * * Approach: repeatedly build up a large population of dirty files in a shared * directory, then BURST-delete many of them while concurrently calling sync(). * The sync triggers a topology-wide flush; the burst unlinks race the flush's * unlock/relock windows. Population is rebuilt each round to keep chains fresh * and dirty. * * Also interleaves mkdir/rmdir to churn directory chains, and does large writes * to force indirect-block chains (deeper flush trees = more unlock/relock * windows at flush.c:662-665). */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <signal.h> #include <sys/wait.h> #include <sys/stat.h> #include <errno.h> static volatile sig_atomic_t stop = 0; static void on_alarm(int s){ (void)s; stop=1; } #define BLKSIZE 8192 /* Population worker: creates and writes files to dirty chains, then * alternates between unlinking and recreating them. */ static void pop_worker(int id, const char *base, long rounds, int popsize) { char path[640]; char buf[BLKSIZE]; int r, i; memset(buf, (char)(id + 0x41), sizeof(buf)); for (r = 0; r < rounds && !stop; r++) { /* create popsize files, write multi-block (dirty data + indirect) */ for (i = 0; i < popsize; i++) { int fd, b; snprintf(path, sizeof(path), "%s/p_%d_%ld_%d", base, id, r, i); fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666); if (fd < 0) continue; for (b = 0; b < 8; b++) { buf[0] = (char)(id ^ r ^ i ^ b); if (write(fd, buf, sizeof(buf)) != sizeof(buf)) break; } close(fd); } /* now unlink them (races any concurrent flush) */ for (i = 0; i < popsize; i++) { snprintf(path, sizeof(path), "%s/p_%d_%ld_%d", base, id, r, i); unlink(path); } } _exit(0); } /* Directory churn worker: mkdir/rmdir to churn directory chains */ static void dir_worker(int id, const char *base, long rounds) { char path[640]; long r; for (r = 0; r < rounds && !stop; r++) { snprintf(path, sizeof(path), "%s/d_%d_%ld", base, id, r); if (mkdir(path, 0777) == 0) { /* create a file inside to dirty the dir chain */ char fp[700]; int fd; snprintf(fp, sizeof(fp), "%s/f", path); fd = open(fp, O_CREAT|O_RDWR|O_TRUNC, 0666); if (fd >= 0) { write(fd, path, sizeof(path)); fsync(fd); close(fd); } unlink(fp); rmdir(path); } } _exit(0); } /* Sync hammer: call sync() continuously to drive the flusher */ static void sync_worker(int id, long rounds) { long r; for (r = 0; r < rounds && !stop; r++) sync(); _exit(0); } int main(int argc, char **argv) { const char *base = NULL; int npop = 12, ndir = 8, nsync = 4; int popsize = 40; long rounds = 5000000; int timeout = 0, opt; while ((opt = getopt(argc, argv, "d:p:n:i:s:t:r:")) != -1) { switch (opt) { case 'd': base = optarg; break; case 'p': npop = atoi(optarg); break; case 'n': ndir = atoi(optarg); break; case 'i': popsize = atoi(optarg); break; case 's': nsync = atoi(optarg); break; case 't': timeout = atoi(optarg); break; case 'r': rounds = atol(optarg); break; default: fprintf(stderr,"usage: %s -d dir [-p pop] [-n dir] [-s sync] " "[-i popsize] [-t sec] [-r rounds]\n", argv[0]); return 2; } } if (!base) { fprintf(stderr,"%s: need -d dir\n", argv[0]); return 2; } fprintf(stderr, "DF-2568 v3 race: base=%s pop=%d dir=%d sync=%d popsize=%d " "rounds=%ld timeout=%d\n", base, npop, ndir, nsync, popsize, rounds, timeout); signal(SIGALRM, on_alarm); if (timeout > 0) alarm((unsigned)timeout); int nkids = npop + ndir + nsync; pid_t *kids = calloc(nkids, sizeof(pid_t)); if (!kids) { perror("calloc"); return 1; } int k, ki = 0; for (k = 0; k < npop; k++) { pid_t p = fork(); if (p < 0) { perror("fork"); return 1; } if (p == 0) pop_worker(k, base, rounds, popsize); kids[ki++] = p; } for (k = 0; k < ndir; k++) { pid_t p = fork(); if (p < 0) { perror("fork"); return 1; } if (p == 0) dir_worker(k, base, rounds); kids[ki++] = p; } for (k = 0; k < nsync; k++) { pid_t p = fork(); if (p < 0) { perror("fork"); return 1; } if (p == 0) sync_worker(k, rounds); kids[ki++] = p; } for (k = 0; k < nkids; k++) if (kids[k] > 0) waitpid(kids[k], NULL, 0); fprintf(stderr, "DF-2568 v3: all children exited (no panic)\n"); return 0; } |