// https://syzkaller.appspot.com/bug?id=c590294e3b4fd859c52ae0c4ffc8b1a5c9185e9b // Copyright 2026 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. // IMPORTANT: Do not copy the macros or definitions below directly into your reproducer. // Instead, add the following line to your reproducer: // #include "race_toolkit.h" // --- Race Condition Toolkit --- // Macros and snippets for CPU pinning, memory barriers, and userfaultfd. #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Unbuffered I/O: Ensure logs are written immediately. #define SETUP_UNBUFFERED_IO() setvbuf(stdout, NULL, _IONBF, 0) // CPU Pinning: Pin the current thread to a specific CPU core. #define PIN_TO_CPU(cpu) \ do { \ cpu_set_t mask; \ CPU_ZERO(&mask); \ CPU_SET(cpu, &mask); \ if (sched_setaffinity(0, sizeof(mask), &mask) == -1) { \ perror("sched_setaffinity"); \ } \ } while (0) // Memory Barrier: Ensure memory ordering. #define MB() __atomic_thread_fence(__ATOMIC_SEQ_CST) // Spin-wait Barrier: Wait until a memory location has a specific value. // Best for tight race windows (low latency, no context switches). #define WAIT_ON(addr, val) \ do { \ while (__atomic_load_n(addr, __ATOMIC_ACQUIRE) != (val)) \ ; \ } while (0) // Signal: Set a memory location to a specific value to release a WAIT_ON. #define SIGNAL(addr, val) __atomic_store_n(addr, val, __ATOMIC_RELEASE) // --- Timing Primitives --- // Robust timing loops in VM environments (using CLOCK_MONOTONIC to avoid time(NULL) jumps). static inline double timer_elapsed_sec(struct timespec* start) { struct timespec now; if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) { perror("clock_gettime(CLOCK_MONOTONIC) elapsed"); exit(1); } return (double)(now.tv_sec - start->tv_sec) + (double)(now.tv_nsec - start->tv_nsec) / 1e9; } // Initialize a monotonic timer variable. #define TIMER_START(t) \ struct timespec t; \ if (clock_gettime(CLOCK_MONOTONIC, &t) == -1) { \ perror("clock_gettime(CLOCK_MONOTONIC) start"); \ exit(1); \ } // Check if the elapsed time since 't' is less than 'sec' seconds. #define TIMER_NOT_EXPIRED(t, sec) (timer_elapsed_sec(&(t)) < (double)(sec)) // Futex-based Event: Shared with syzkaller executor. // Best for general synchronization or longer waits to save CPU. typedef struct { int state; } event_t; static void event_init(event_t* ev) { ev->state = 0; } static void event_reset(event_t* ev) { ev->state = 0; } static void event_set(event_t* ev) { if (__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) { fprintf(stderr, "event already set\n"); exit(1); } __atomic_store_n(&ev->state, 1, __ATOMIC_RELEASE); syscall(SYS_futex, &ev->state, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1000000); } static void event_wait(event_t* ev) { while (!__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, 0); } // userfaultfd setup: Register a memory range for page fault handling. static int setup_uffd(void* addr, size_t len) { int uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); if (uffd == -1) return -1; struct uffdio_api api = {.api = UFFD_API, .features = 0}; if (ioctl(uffd, UFFDIO_API, &api) == -1) { close(uffd); return -1; } struct uffdio_register reg = { .range = {.start = (uintptr_t)addr, .len = len}, .mode = UFFDIO_REGISTER_MODE_MISSING}; if (ioctl(uffd, UFFDIO_REGISTER, ®) == -1) { close(uffd); return -1; } return uffd; } // --- Guidance on Usage --- // 1. Use WAIT_ON/SIGNAL for tight race conditions to avoid scheduling overhead. // 2. Use event_t (futexes) for general coordination or when waiting for longer periods. // 3. Always use PIN_TO_CPU to increase race probability on multi-core systems. // 4. Use setup_uffd to register a memory range for page fault handling. This allows you to // pause a thread accessing that memory until you handle the fault, creating a reliable // and controllable race window. // 5. Call SETUP_UNBUFFERED_IO() at the start of main() to ensure that logs are printed // immediately. This is essential for understanding the exact interleaving of events // when debugging race conditions. // 6. For timing-based loops (e.g., running a race for 10 seconds), do NOT use time(NULL) // or loops relying on real-time clocks, as VM clocks are highly unreliable and can fail or drift. // Instead, use the robust monotonic timing primitives TIMER_START and TIMER_NOT_EXPIRED: // TIMER_START(start); // while (TIMER_NOT_EXPIRED(start, 10.0)) { // // Your race logic here // } #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef O_TMPFILE #define O_TMPFILE (020000000 | O_DIRECTORY) #endif /* JFS Constants */ #define JFS_MAGIC "JFS1" #define AGGREGATE_I 1 #define BMAP_I 2 #define FILESYSTEM_I 16 #define ROOT_I 2 #define MAXAG 128 #define SMAPSZ 4 #define EXTSPERIAG 128 #define LPERDMAP 1024 #define TREESIZE (256+64+16+4+1) #define XTROOTMAXSLOT 18 /* Endianness Helpers */ static inline uint32_t to_le32(uint32_t val) { return val; } static inline uint64_t to_le64(uint64_t val) { return val; } static inline uint16_t to_le16(uint16_t val) { return val; } /* JFS Structures */ typedef struct { uint32_t len_addr; uint32_t addr2; } pxd_t; struct timestruc_t { uint32_t tv_sec; uint32_t tv_nsec; }; typedef struct { uint8_t flag; uint8_t rsrvd[3]; uint32_t size; pxd_t loc; } dxd_t; typedef struct { uint8_t flag; uint8_t rsvrd[2]; uint8_t off1; uint32_t off2; pxd_t loc; } xad_t; struct xtheader { uint64_t next; uint64_t prev; uint8_t flag; uint8_t rsrvd1; uint16_t nextindex; uint16_t maxentry; uint16_t rsrvd2; pxd_t self; }; typedef union { struct xtheader header; xad_t xad[XTROOTMAXSLOT]; } xtroot_t; struct dasd { uint8_t thresh; uint8_t delta; uint8_t rsrvd1; uint8_t limit_hi; uint32_t limit_lo; uint8_t rsrvd2[3]; uint8_t used_hi; uint32_t used_lo; }; struct dtslot { int8_t next; int8_t cnt; uint16_t name[15]; }; typedef union { struct { struct dasd DASD; uint8_t flag; uint8_t nextindex; int8_t freecnt; int8_t freelist; uint32_t idotdot; int8_t stbl[8]; } header; struct dtslot slot[9]; } dtroot_t; struct dir_table_slot { uint8_t rsrvd; uint8_t flag; uint8_t slot; uint8_t addr1; uint32_t addr2; }; struct dinode { uint32_t di_inostamp; uint32_t di_fileset; uint32_t di_number; uint32_t di_gen; pxd_t di_ixpxd; uint64_t di_size; uint64_t di_nblocks; uint32_t di_nlink; uint32_t di_uid; uint32_t di_gid; uint32_t di_mode; struct timestruc_t di_atime; struct timestruc_t di_ctime; struct timestruc_t di_mtime; struct timestruc_t di_otime; dxd_t di_acl; dxd_t di_ea; uint32_t di_next_index; uint32_t di_acltype; union { struct { struct dir_table_slot _table[12]; dtroot_t _dtroot; } _dir; struct { union { uint8_t _data[96]; struct { void *_imap; uint32_t _gengen; } _imap; } _u1; union { xtroot_t _xtroot; struct { uint8_t unused[16]; dxd_t _dxd; union { struct { union { uint32_t _rdev; uint8_t _fastsymlink[128]; } _u; uint8_t _inlineea[128]; }; uint8_t _inline_all[256]; }; } _special; } _u2; } _file; } u; }; struct iagctl_disk { uint32_t inofree; uint32_t extfree; uint32_t numinos; uint32_t numfree; }; struct dinomap_disk { uint32_t in_freeiag; uint32_t in_nextiag; uint32_t in_numinos; uint32_t in_numfree; uint32_t in_nbperiext; uint32_t in_l2nbperiext; uint32_t in_diskblock; uint32_t in_maxag; uint8_t pad[2016]; struct iagctl_disk in_agctl[MAXAG]; }; struct iag { uint64_t agstart; uint32_t iagnum; uint32_t inofreefwd; uint32_t inofreeback; uint32_t extfreefwd; uint32_t extfreeback; uint32_t iagfree; uint32_t inosmap[SMAPSZ]; uint32_t extsmap[SMAPSZ]; uint32_t nfreeinos; uint32_t nfreeexts; uint8_t pad[1976]; uint32_t wmap[EXTSPERIAG]; uint32_t pmap[EXTSPERIAG]; pxd_t inoext[EXTSPERIAG]; }; struct dmaptree { uint32_t nleafs; uint32_t l2nleafs; uint32_t leafidx; uint32_t height; int8_t budmin; int8_t stree[TREESIZE]; uint8_t pad[2]; }; struct dbmap_disk { uint64_t dn_mapsize; uint64_t dn_nfree; uint32_t dn_l2nbperpage; uint32_t dn_numag; uint32_t dn_maxlevel; uint32_t dn_maxag; uint32_t dn_agpref; uint32_t dn_aglevel; uint32_t dn_agheight; uint32_t dn_agwidth; uint32_t dn_agstart; uint32_t dn_agl2size; uint64_t dn_agfree[MAXAG]; uint64_t dn_agsize; int8_t dn_maxfreebud; uint8_t pad[3007]; }; struct dmap { uint32_t nblocks; uint32_t nfree; uint64_t start; struct dmaptree tree; uint8_t pad[1672]; uint32_t wmap[LPERDMAP]; uint32_t pmap[LPERDMAP]; }; struct jfs_superblock { char s_magic[4]; uint32_t s_version; uint64_t s_size; uint32_t s_bsize; uint16_t s_l2bsize; uint16_t s_l2bfactor; uint32_t s_pbsize; uint16_t s_l2pbsize; uint16_t pad; uint32_t s_agsize; uint32_t s_flag; uint32_t s_state; uint32_t s_compress; pxd_t s_ait2; pxd_t s_aim2; uint32_t s_logdev; uint32_t s_logserial; pxd_t s_logpxd; pxd_t s_fsckpxd; struct timestruc_t s_time; uint32_t s_fsckloglen; int8_t s_fscklog; char s_fpack[11]; uint64_t s_xsize; pxd_t s_xfsckpxd; pxd_t s_xlogpxd; uint8_t s_uuid[16]; char s_label[16]; uint8_t s_loguuid[16]; }; struct logsuper { uint32_t magic; uint32_t version; uint32_t serial; uint32_t size; uint32_t bsize; uint32_t l2bsize; uint32_t flag; uint32_t state; uint32_t end; uint8_t uuid[16]; char label[16]; struct { uint8_t uuid[16]; } active[24]; }; struct logpage { struct { uint32_t page; uint16_t rsrvd; uint16_t eor; } h; uint32_t data[4096 / 4 - 4]; struct { uint32_t page; uint16_t rsrvd; uint16_t eor; } t; }; struct lrd { uint32_t logtid; uint32_t backchain; uint16_t type; uint16_t length; uint32_t aggregate; union { struct { uint32_t sync; } syncpt; } log; }; struct ldtentry { uint32_t inumber; int8_t next; uint8_t namlen; uint16_t name[11]; uint32_t index; } __attribute__((packed)); static void set_pxd(pxd_t *pxd, uint32_t len, uint64_t addr) { uint32_t len_addr = (len & 0xffffff) | (((addr >> 32) & 0xff) << 24); pxd->len_addr = to_le32(len_addr); pxd->addr2 = to_le32(addr & 0xffffffff); } static void init_dinode(struct dinode *dp, uint32_t fileset, uint32_t number, uint32_t mode, uint32_t nlink, uint64_t size) { memset(dp, 0, sizeof(*dp)); dp->di_inostamp = to_le32(0x12345678); dp->di_fileset = to_le32(fileset); dp->di_number = to_le32(number); dp->di_gen = to_le32(1); dp->di_size = to_le64(size); dp->di_nlink = to_le32(nlink); dp->di_mode = to_le32(mode); dp->di_atime.tv_sec = to_le32(1700000000); dp->di_ctime.tv_sec = to_le32(1700000000); dp->di_mtime.tv_sec = to_le32(1700000000); dp->di_otime.tv_sec = to_le32(1700000000); } void generate_jfs_image(uint8_t *image) { memset(image, 0, 16 * 1024 * 1024); struct jfs_superblock *sb = (struct jfs_superblock *)(image + 8 * 4096); memcpy(sb->s_magic, "JFS1", 4); sb->s_version = to_le32(2); sb->s_size = to_le64(4096); sb->s_bsize = to_le32(4096); sb->s_l2bsize = to_le16(12); sb->s_l2bfactor = to_le16(3); sb->s_pbsize = to_le32(512); sb->s_l2pbsize = to_le16(9); sb->s_agsize = to_le32(8192); sb->s_flag = to_le32(0x10210900); sb->s_state = to_le32(0); set_pxd(&sb->s_logpxd, 256, 3000); memset(&sb->s_uuid, 0xaa, 16); memset(&sb->s_loguuid, 0xbb, 16); struct dinomap_disk *aimap = (struct dinomap_disk *)(image + 9 * 4096); aimap->in_freeiag = to_le32(-1); aimap->in_nextiag = to_le32(1); aimap->in_numinos = to_le32(32); aimap->in_numfree = to_le32(29); aimap->in_nbperiext = to_le32(4); aimap->in_l2nbperiext = to_le32(2); aimap->in_agctl[0].inofree = to_le32(-1); aimap->in_agctl[0].extfree = to_le32(-1); aimap->in_agctl[0].numinos = to_le32(32); aimap->in_agctl[0].numfree = to_le32(29); struct iag *ai_iag = (struct iag *)(image + 10 * 4096); ai_iag->agstart = to_le64(0); ai_iag->iagnum = to_le32(0); ai_iag->inofreefwd = to_le32(-1); ai_iag->inofreeback = to_le32(-1); ai_iag->extfreefwd = to_le32(-1); ai_iag->extfreeback = to_le32(-1); ai_iag->iagfree = to_le32(-1); ai_iag->nfreeinos = to_le32(29); ai_iag->nfreeexts = to_le32(127); ai_iag->wmap[0] = to_le32(0x00010006); ai_iag->pmap[0] = to_le32(0x00010006); set_pxd(&ai_iag->inoext[0], 4, 11); struct dinode *dp1 = (struct dinode *)(image + 11 * 4096 + 1 * 512); init_dinode(dp1, AGGREGATE_I, AGGREGATE_I, S_IFREG | 0600, 1, 8192); set_pxd(&dp1->di_ixpxd, 4, 11); xtroot_t *xt1 = &dp1->u._file._u2._xtroot; xt1->header.flag = 0x83; xt1->header.nextindex = to_le16(3); xt1->header.maxentry = to_le16(18); xt1->xad[2].flag = 0; xt1->xad[2].off1 = 0; xt1->xad[2].off2 = to_le32(0); set_pxd(&xt1->xad[2].loc, 2, 9); struct dinode *dp2 = (struct dinode *)(image + 11 * 4096 + 2 * 512); init_dinode(dp2, BMAP_I, BMAP_I, S_IFREG | 0600, 1, 8192); set_pxd(&dp2->di_ixpxd, 4, 11); xtroot_t *xt2 = &dp2->u._file._u2._xtroot; xt2->header.flag = 0x83; xt2->header.nextindex = to_le16(3); xt2->header.maxentry = to_le16(18); xt2->xad[2].flag = 0; xt2->xad[2].off1 = 0; xt2->xad[2].off2 = to_le32(0); set_pxd(&xt2->xad[2].loc, 2, 16); struct dinode *dp16 = (struct dinode *)(image + 13 * 4096 + 0 * 512); init_dinode(dp16, FILESYSTEM_I, FILESYSTEM_I, S_IFREG | 0600, 1, 8192); set_pxd(&dp16->di_ixpxd, 4, 20); dp16->u._file._u1._imap._gengen = to_le32(1); xtroot_t *xt16 = &dp16->u._file._u2._xtroot; xt16->header.flag = 0x83; xt16->header.nextindex = to_le16(3); xt16->header.maxentry = to_le16(18); xt16->xad[2].flag = 0; xt16->xad[2].off1 = 0; xt16->xad[2].off2 = to_le32(0); set_pxd(&xt16->xad[2].loc, 2, 20); memcpy(image + 15 * 4096, image + 8 * 4096, 4096); struct dbmap_disk *bmap = (struct dbmap_disk *)(image + 16 * 4096); bmap->dn_mapsize = to_le64(4096); bmap->dn_nfree = to_le64(840); bmap->dn_l2nbperpage = to_le32(0); bmap->dn_numag = to_le32(1); bmap->dn_maxlevel = to_le32(0); bmap->dn_maxag = to_le32(0); bmap->dn_agpref = to_le32(0); bmap->dn_aglevel = to_le32(0); bmap->dn_agheight = to_le32(0); bmap->dn_agwidth = to_le32(1); bmap->dn_agstart = to_le32(0); bmap->dn_agl2size = to_le32(13); bmap->dn_agfree[0] = to_le64(840); bmap->dn_agsize = to_le64(8192); bmap->dn_maxfreebud = 13; struct dmap *dp_dmap = (struct dmap *)(image + 17 * 4096); dp_dmap->nblocks = to_le32(4096); dp_dmap->nfree = to_le32(840); dp_dmap->start = to_le64(0); for (int i = 0; i < 101; i++) { dp_dmap->wmap[i] = to_le32(0xffffffff); dp_dmap->pmap[i] = to_le32(0xffffffff); } dp_dmap->wmap[101] = to_le32(0x00ffffff); dp_dmap->pmap[101] = to_le32(0x00ffffff); for (int i = 128; i < 1024; i++) { dp_dmap->wmap[i] = to_le32(0xffffffff); dp_dmap->pmap[i] = to_le32(0xffffffff); } struct dinomap_disk *fimap = (struct dinomap_disk *)(image + 20 * 4096); fimap->in_freeiag = to_le32(-1); fimap->in_nextiag = to_le32(1); fimap->in_numinos = to_le32(32); fimap->in_numfree = to_le32(31); fimap->in_nbperiext = to_le32(4); fimap->in_l2nbperiext = to_le32(2); fimap->in_agctl[0].inofree = to_le32(-1); fimap->in_agctl[0].extfree = to_le32(-1); fimap->in_agctl[0].numinos = to_le32(32); fimap->in_agctl[0].numfree = to_le32(31); struct iag *fi_iag = (struct iag *)(image + 21 * 4096); fi_iag->agstart = to_le64(0); fi_iag->iagnum = to_le32(0); fi_iag->inofreefwd = to_le32(-1); fi_iag->inofreeback = to_le32(-1); fi_iag->extfreefwd = to_le32(-1); fi_iag->extfreeback = to_le32(-1); fi_iag->iagfree = to_le32(-1); fi_iag->nfreeinos = to_le32(31); fi_iag->nfreeexts = to_le32(127); fi_iag->wmap[0] = to_le32(0x00000004); fi_iag->pmap[0] = to_le32(0x00000004); set_pxd(&fi_iag->inoext[0], 4, 22); struct dinode *dp_root = (struct dinode *)(image + 22 * 4096 + 2 * 512); init_dinode(dp_root, FILESYSTEM_I, ROOT_I, S_IFDIR | 0755, 2, 256); set_pxd(&dp_root->di_ixpxd, 4, 22); /* SET TO 0 TO TRIGGER add_missing_indices in jfs_readdir */ dp_root->di_next_index = to_le32(0); dp_root->u._dir._table[0].flag = 1; dp_root->u._dir._table[0].slot = 0; dp_root->u._dir._table[0].addr1 = 0; dp_root->u._dir._table[0].addr2 = to_le32(0); dp_root->u._dir._table[1].flag = 1; dp_root->u._dir._table[1].slot = 1; dp_root->u._dir._table[1].addr1 = 0; dp_root->u._dir._table[1].addr2 = to_le32(0); dtroot_t *p = &dp_root->u._dir._dtroot; p->header.flag = 0x03; // BT_ROOT | BT_LEAF p->header.nextindex = 2; p->header.freelist = 2; p->header.freecnt = 7; p->header.idotdot = to_le32(2); p->header.stbl[0] = 0; p->header.stbl[1] = 1; for (int i = 2; i < 8; i++) { p->header.stbl[i] = -1; } struct ldtentry *ldt0 = (struct ldtentry *)&p->slot[0]; ldt0->inumber = to_le32(2); ldt0->next = -1; ldt0->namlen = 1; ldt0->name[0] = to_le16('.'); ldt0->index = to_le32(2); struct ldtentry *ldt1 = (struct ldtentry *)&p->slot[1]; ldt1->inumber = to_le32(2); ldt1->next = -1; ldt1->namlen = 2; ldt1->name[0] = to_le16('.'); ldt1->name[1] = to_le16('.'); ldt1->index = to_le32(3); for (int fsi = 2; fsi < 9; fsi++) { p->slot[fsi].next = (fsi == 8) ? -1 : (fsi + 1); } struct logsuper *log_sb = (struct logsuper *)(image + 3001 * 4096); log_sb->magic = to_le32(0x87654321); log_sb->version = to_le32(1); /* SET TO LOGREDONE (1) TO ALLOW READ-WRITE MOUNT */ log_sb->state = to_le32(1); log_sb->size = to_le32(256); log_sb->bsize = to_le32(4096); log_sb->l2bsize = to_le32(12); log_sb->end = to_le32(2 * 4096 + 8 + 36); memset(&log_sb->uuid, 0xbb, 16); struct logpage *lp2 = (struct logpage *)(image + 3002 * 4096); lp2->h.page = to_le32(253); lp2->t.page = to_le32(253); lp2->h.eor = to_le16(44); lp2->t.eor = to_le16(44); struct lrd *lrd_ptr = (struct lrd *)&lp2->data; lrd_ptr->logtid = 0; lrd_ptr->backchain = 0; lrd_ptr->type = to_le16(0x4000); lrd_ptr->length = 0; lrd_ptr->log.syncpt.sync = 0; for (int lspn = 0; lspn < 253; lspn++) { struct logpage *lp = (struct logpage *)(image + (3003 + lspn) * 4096); lp->h.page = to_le32(lspn); lp->t.page = to_le32(lspn); lp->h.eor = to_le16(8); lp->t.eor = to_le16(8); } } int sync_flag = 0; int keep_running = 1; const char *mount_point = "/tmp/jfs_mount"; void *fsync_thread(void *arg) { WAIT_ON(&sync_flag, 1); while (keep_running) { int fd = open("/tmp/jfs_mount/trigger", O_RDWR); if (fd >= 0) { if (write(fd, "a", 1) == 1) { close(fd); fd = open("/tmp/jfs_mount/trigger", O_RDONLY); if (fd >= 0) { fsync(fd); close(fd); } } else { close(fd); } } else { usleep(1000); } } return NULL; } void *remount_thread(void *arg) { WAIT_ON(&sync_flag, 1); while (keep_running) { if (mount(NULL, mount_point, NULL, MS_REMOUNT | MS_RDONLY, NULL) == 0) { usleep(1000); mount(NULL, mount_point, NULL, MS_REMOUNT, NULL); } else { usleep(1000); } } return NULL; } void *readdir_thread(void *arg) { WAIT_ON(&sync_flag, 1); while (keep_running) { int fd = open(mount_point, O_RDONLY | O_DIRECTORY); if (fd >= 0) { char buf[1024]; syscall(SYS_getdents64, fd, buf, sizeof(buf)); close(fd); } else { usleep(1000); } } return NULL; } int setup_loop_device(int image_fd, char *loop_device) { int control_fd = open("/dev/loop-control", O_RDWR); if (control_fd < 0) return -1; int dev_num = ioctl(control_fd, LOOP_CTL_GET_FREE); close(control_fd); if (dev_num < 0) return -1; sprintf(loop_device, "/dev/loop%d", dev_num); int loop_fd = open(loop_device, O_RDWR); if (loop_fd < 0) return -1; if (ioctl(loop_fd, LOOP_SET_FD, image_fd) < 0) { close(loop_fd); return -1; } close(loop_fd); return dev_num; } void cleanup_loop_device(const char *loop_device) { int loop_fd = open(loop_device, O_RDONLY); if (loop_fd >= 0) { int retries = 100; while (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0 && retries-- > 0) { usleep(10000); } close(loop_fd); } } int main() { SETUP_UNBUFFERED_IO(); srand(time(NULL) ^ getpid()); uint8_t *img_data = calloc(1, 16 * 1024 * 1024); if (!img_data) { printf("[-] calloc failed: %s\n", strerror(errno)); return 1; } generate_jfs_image(img_data); int image_fd = open("/tmp", O_RDWR | O_TMPFILE, 0666); if (image_fd < 0) { image_fd = open("/tmp/jfs.img", O_RDWR | O_CREAT | O_TRUNC, 0666); if (image_fd >= 0) unlink("/tmp/jfs.img"); } if (image_fd < 0) { printf("[-] Failed to create image file: %s\n", strerror(errno)); return 1; } if (ftruncate(image_fd, 16 * 1024 * 1024) < 0) { printf("[-] ftruncate failed: %s\n", strerror(errno)); return 1; } mkdir(mount_point, 0755); printf("[+] Starting race loop...\n"); TIMER_START(start); int iter = 0; while (TIMER_NOT_EXPIRED(start, 10.0)) { iter++; /* Restore clean image on every iteration */ if (pwrite(image_fd, img_data, 16 * 1024 * 1024, 0) != 16 * 1024 * 1024) { printf("[-] pwrite failed: %s\n", strerror(errno)); break; } char loop_device[256]; if (setup_loop_device(image_fd, loop_device) < 0) { printf("[-] setup_loop_device failed\n"); break; } int retries = 50; int mount_res; while ((mount_res = mount(loop_device, mount_point, "jfs", 0, NULL)) < 0) { if (errno != EBUSY) break; usleep(20000); retries--; if (retries <= 0) break; } if (mount_res < 0) { printf("[-] mount failed: %s\n", strerror(errno)); cleanup_loop_device(loop_device); continue; } int fd_file = open("/tmp/jfs_mount/trigger", O_CREAT | O_RDWR, 0666); if (fd_file >= 0) close(fd_file); keep_running = 1; sync_flag = 0; pthread_t t1, t2, t3; pthread_create(&t1, NULL, fsync_thread, NULL); pthread_create(&t2, NULL, remount_thread, NULL); pthread_create(&t3, NULL, readdir_thread, NULL); SIGNAL(&sync_flag, 1); usleep(200000); // Run for 200ms per iteration keep_running = 0; pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); retries = 10; while (umount2(mount_point, MNT_DETACH) != 0 && retries-- > 0) { usleep(10000); } cleanup_loop_device(loop_device); } rmdir(mount_point); close(image_fd); free(img_data); printf("[+] Finished %d iterations.\n", iter); return 0; }