DF-2568 / race_flush_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 | /* * DF-2568 — aggressive hammer2 flush NULL-deref race (v2). * * Same bug as v1 but tuned for a tighter race window: * - ALL workers share ONE directory, so a single flush of the dir races * against many concurrent unlinks/recreates. * - Workers also do rename() to create extra chain-topology churn. * - Syncers call sync() and fsync() as fast as possible. * - Files are written multi-block to force indirect-block chains (deeper * flush trees = more chains traversing the :662-665 unlock window). */ #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; } static void worker(int id, const char *base, long iters) { char path[640], path2[640]; char buf[8192]; long i; memset(buf, (char)(id|0x40), sizeof(buf)); for (i = 0; i < iters && !stop; i++) { int f, fd; snprintf(path, sizeof(path), "%s/%d_%ld", base, id, i); fd = open(path, O_RDWR|O_CREAT|O_TRUNC, 0666); if (fd < 0) continue; /* write several blocks -> dirties data + indirect chains */ for (f = 0; f < 4; f++) if (write(fd, buf, sizeof(buf)) != (ssize_t)sizeof(buf)) break; fsync(fd); close(fd); /* rename half the time (topology churn on the parent dir chain) */ if (i & 1) { snprintf(path2, sizeof(path2), "%s/%d_%ld.r", base, id, i); rename(path, path2); unlink(path2); } else { unlink(path); } } _exit(0); } static void syncer(int id, const char *base, long iters) { long i; char path[640]; int fd; for (i = 0; i < iters && !stop; i++) { sync(); /* drive per-inode flush via fsync on a scratch file */ snprintf(path, sizeof(path), "%s/.s%d", base, id); fd = open(path, O_RDWR|O_CREAT|O_TRUNC, 0666); if (fd >= 0) { fsync(fd); close(fd); unlink(path); } } _exit(0); } int main(int argc, char **argv) { const char *base = NULL; int nworkers = 20, nsyncers = 6; long iters = 5000000; int timeout = 0, opt; while ((opt = getopt(argc, argv, "d:w:s:i:t:")) != -1) { switch (opt){ case 'd': base=optarg; break; case 'w': nworkers=atoi(optarg); break; case 's': nsyncers=atoi(optarg); break; case 'i': iters=atol(optarg); break; case 't': timeout=atoi(optarg); break; default: fprintf(stderr,"usage: %s -d dir [-w N] [-s N] [-i N] [-t sec]\n",argv[0]); return 2; } } if (!base){ fprintf(stderr,"%s: need -d dir\n",argv[0]); return 2; } fprintf(stderr,"DF-2568 v2 race: base=%s workers=%d syncers=%d iters=%ld timeout=%d\n", base, nworkers, nsyncers, iters, timeout); signal(SIGALRM, on_alarm); if (timeout>0) alarm((unsigned)timeout); int nkids = nworkers + nsyncers; pid_t *kids = calloc(nkids, sizeof(pid_t)); if (!kids){perror("calloc");return 1;} int k; for (k=0;k<nworkers;k++){ pid_t p=fork(); if(p<0){perror("fork");return 1;} if(p==0)worker(k,base,iters); kids[k]=p; } for (k=0;k<nsyncers;k++){ pid_t p=fork(); if(p<0){perror("fork");return 1;} if(p==0)syncer(k,base,iters); kids[nworkers+k]=p; } for (k=0;k<nkids;k++) if(kids[k]>0) waitpid(kids[k],NULL,0); fprintf(stderr,"DF-2568 v2: all children exited (no panic)\n"); return 0; } |