// https://syzkaller.appspot.com/bug?id=bc5db15be2ab04e98ebf08c4ba4a3a95ba1769c8 #define _GNU_SOURCE // 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 // } #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BSIZE 4096 #define TOTAL_BLKS 4096 #define LOG_START_BLK 1024 typedef struct { uint32_t len_addr; uint32_t addr2; } pxd_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; }; struct timestruc_t { uint32_t tv_sec; uint32_t tv_nsec; }; typedef struct { uint8_t flag; uint8_t rsvrd[3]; uint32_t size; pxd_t loc; } dxd_t; struct dasd { uint8_t thresh; uint8_t delta; uint8_t pad1; uint8_t limit_hi; uint32_t limit_lo; uint8_t pad2[3]; uint8_t usage_hi; uint32_t usage_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]; } _u1; union { uint8_t _xtroot[288]; } _u2; } _file; } u; }; 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 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[128]; }; 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[4]; uint32_t extsmap[4]; uint32_t nfreeinos; uint32_t nfreeexts; uint8_t pad[1976]; uint32_t wmap[128]; uint32_t pmap[128]; pxd_t inoext[128]; }; 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[128]; uint64_t dn_agsize; int8_t dn_maxfreebud; uint8_t pad[3007]; }; 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]; uint8_t active[2048]; }; struct logpage { struct { uint32_t page; uint16_t rsrvd; uint16_t eor; } h; uint32_t data[1020]; struct { uint32_t page; uint16_t rsrvd; uint16_t eor; } t; }; static void reset_journal(int img_fd) { char page_buf[BSIZE]; uint32_t clean_state = htole32(0); if (pwrite(img_fd, &clean_state, sizeof(clean_state), 8 * BSIZE + 40) != sizeof(clean_state)) { printf("[-] Failed to pwrite s_state to sb 1: %s\n", strerror(errno)); exit(1); } if (pwrite(img_fd, &clean_state, sizeof(clean_state), 15 * BSIZE + 40) != sizeof(clean_state)) { printf("[-] Failed to pwrite s_state to sb 2: %s\n", strerror(errno)); exit(1); } memset(page_buf, 0, BSIZE); struct logsuper *logsup = (struct logsuper *)page_buf; logsup->magic = htole32(0x87654321); logsup->version = htole32(1); logsup->serial = htole32(1); logsup->size = htole32(1024); logsup->bsize = htole32(4096); logsup->l2bsize = htole32(12); logsup->flag = htole32(0x10010900); logsup->state = htole32(1); logsup->end = htole32(2 * 4096 + 4000); if (pwrite(img_fd, page_buf, BSIZE, (LOG_START_BLK + 1) * BSIZE) != BSIZE) { printf("[-] Failed to pwrite logsuper: %s\n", strerror(errno)); exit(1); } memset(page_buf, 0, BSIZE); struct logpage *lp = (struct logpage *)page_buf; lp->h.page = lp->t.page = htole32(1021); lp->h.eor = lp->t.eor = htole16(4000); if (pwrite(img_fd, page_buf, BSIZE, (LOG_START_BLK + 2) * BSIZE) != BSIZE) { printf("[-] Failed to pwrite logpage 2: %s\n", strerror(errno)); exit(1); } } static void create_jfs_image(const char *path, int *out_fd) { int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0666); if (fd < 0) { printf("[-] Failed to open image %s: %s\n", path, strerror(errno)); exit(1); } if (ftruncate(fd, TOTAL_BLKS * BSIZE) < 0) { printf("[-] Failed to ftruncate %s: %s\n", path, strerror(errno)); exit(1); } char buf[BSIZE]; // Primary and secondary superblocks (Blocks 8 and 15) memset(buf, 0, BSIZE); struct jfs_superblock *sb = (struct jfs_superblock *)buf; memcpy(sb->s_magic, "JFS1", 4); sb->s_version = htole32(1); sb->s_size = htole64(4096); sb->s_bsize = htole32(4096); sb->s_l2bsize = htole16(12); sb->s_l2bfactor = htole16(3); sb->s_pbsize = htole32(4096); sb->s_l2pbsize = htole16(12); sb->pad = 0; sb->s_agsize = htole32(4096); sb->s_flag = htole32(0x10010900); sb->s_state = htole32(0); sb->s_logserial = htole32(1); sb->s_logpxd.len_addr = htole32(1024); sb->s_logpxd.addr2 = htole32(1024); if (pwrite(fd, buf, BSIZE, 8 * BSIZE) != BSIZE) { printf("[-] Failed to write superblock 1: %s\n", strerror(errno)); exit(1); } if (pwrite(fd, buf, BSIZE, 15 * BSIZE) != BSIZE) { printf("[-] Failed to write superblock 2: %s\n", strerror(errno)); exit(1); } // AIMAP Control Page (Block 9) memset(buf, 0, BSIZE); struct dinomap_disk *aimap = (struct dinomap_disk *)buf; aimap->in_freeiag = htole32(-1); aimap->in_nextiag = htole32(0); aimap->in_numinos = htole32(32); aimap->in_numfree = htole32(0); aimap->in_nbperiext = htole32(4); aimap->in_l2nbperiext = htole32(2); aimap->in_agctl[0].inofree = htole32(-1); aimap->in_agctl[0].extfree = htole32(-1); aimap->in_agctl[0].numinos = htole32(32); aimap->in_agctl[0].numfree = htole32(0); if (pwrite(fd, buf, BSIZE, 9 * BSIZE) != BSIZE) { printf("[-] Failed to write AIMAP: %s\n", strerror(errno)); exit(1); } // Inode 1 (AGGREGATE_I) at Block 11 offset 512 char inobuf[512]; memset(inobuf, 0, 512); struct dinode *dip = (struct dinode *)inobuf; dip->di_fileset = htole32(1); dip->di_number = htole32(1); dip->di_nlink = htole32(1); dip->di_mode = htole32(S_IFREG | 0600); dip->di_size = htole64(4096); dip->di_nblocks = htole64(1); struct xtheader *hdr = (struct xtheader *)&dip->u._file._u2._xtroot; xad_t *xad = (xad_t *)&dip->u._file._u2._xtroot; hdr->flag = 0x83; hdr->nextindex = htole16(3); hdr->maxentry = htole16(10); xad[2].loc.len_addr = htole32(1); xad[2].loc.addr2 = htole32(9); if (pwrite(fd, inobuf, 512, 11 * BSIZE + 512) != 512) { printf("[-] Failed to write Inode 1: %s\n", strerror(errno)); exit(1); } // Inode 2 (BMAP_I) at Block 11 offset 1024 memset(inobuf, 0, 512); dip->di_fileset = htole32(2); dip->di_number = htole32(2); dip->di_nlink = htole32(1); dip->di_mode = htole32(S_IFREG | 0600); dip->di_size = htole64(4096); dip->di_nblocks = htole64(1); hdr = (struct xtheader *)&dip->u._file._u2._xtroot; xad = (xad_t *)&dip->u._file._u2._xtroot; hdr->flag = 0x83; hdr->nextindex = htole16(3); hdr->maxentry = htole16(10); xad[2].loc.len_addr = htole32(1); xad[2].loc.addr2 = htole32(16); if (pwrite(fd, inobuf, 512, 11 * BSIZE + 1024) != 512) { printf("[-] Failed to write Inode 2: %s\n", strerror(errno)); exit(1); } // Inode 16 (FILESYSTEM_I) at Block 13 offset 0 memset(inobuf, 0, 512); dip->di_fileset = htole32(16); dip->di_number = htole32(16); dip->di_nlink = htole32(1); dip->di_mode = htole32(S_IFREG | 0600); dip->di_size = htole64(8192); dip->di_nblocks = htole64(2); hdr = (struct xtheader *)&dip->u._file._u2._xtroot; xad = (xad_t *)&dip->u._file._u2._xtroot; hdr->flag = 0x83; hdr->nextindex = htole16(3); hdr->maxentry = htole16(10); xad[2].loc.len_addr = htole32(2); xad[2].loc.addr2 = htole32(18); if (pwrite(fd, inobuf, 512, 13 * BSIZE) != 512) { printf("[-] Failed to write Inode 16: %s\n", strerror(errno)); exit(1); } // Block Allocation Map (Block 16) memset(buf, 0, BSIZE); struct dbmap_disk *bmap = (struct dbmap_disk *)buf; bmap->dn_mapsize = htole64(4096); bmap->dn_nfree = htole64(2048); bmap->dn_l2nbperpage = htole32(0); bmap->dn_numag = htole32(1); bmap->dn_maxlevel = htole32(0); bmap->dn_maxag = htole32(0); bmap->dn_agpref = htole32(0); bmap->dn_aglevel = htole32(0); bmap->dn_agheight = htole32(0); bmap->dn_agwidth = htole32(1); bmap->dn_agstart = htole32(0); bmap->dn_agl2size = htole32(12); bmap->dn_agfree[0] = htole64(2048); bmap->dn_agsize = htole64(4096); bmap->dn_maxfreebud = -1; if (pwrite(fd, buf, BSIZE, 16 * BSIZE) != BSIZE) { printf("[-] Failed to write BMAP: %s\n", strerror(errno)); exit(1); } // Fileset Inode Map Control Page (Block 18) memset(buf, 0, BSIZE); struct dinomap_disk *fsimap = (struct dinomap_disk *)buf; fsimap->in_freeiag = htole32(-1); fsimap->in_nextiag = htole32(1); fsimap->in_numinos = htole32(32); fsimap->in_numfree = htole32(29); fsimap->in_nbperiext = htole32(4); fsimap->in_l2nbperiext = htole32(2); fsimap->in_agctl[0].inofree = htole32(0); fsimap->in_agctl[0].extfree = htole32(-1); fsimap->in_agctl[0].numinos = htole32(32); fsimap->in_agctl[0].numfree = htole32(29); if (pwrite(fd, buf, BSIZE, 18 * BSIZE) != BSIZE) { printf("[-] Failed to write Fileset Inode Map: %s\n", strerror(errno)); exit(1); } // Fileset IAG 0 (Block 19) memset(buf, 0, BSIZE); struct iag *iag0 = (struct iag *)buf; iag0->agstart = htole64(0); iag0->iagnum = htole32(0); iag0->inofreefwd = htole32(-1); iag0->inofreeback = htole32(-1); iag0->extfreefwd = htole32(-1); iag0->extfreeback = htole32(-1); iag0->iagfree = htole32(-1); iag0->nfreeinos = htole32(29); iag0->nfreeexts = htole32(0); iag0->wmap[0] = htole32(0x7); iag0->pmap[0] = htole32(0x7); iag0->inoext[0].len_addr = htole32(4); iag0->inoext[0].addr2 = htole32(20); if (pwrite(fd, buf, BSIZE, 19 * BSIZE) != BSIZE) { printf("[-] Failed to write IAG 0: %s\n", strerror(errno)); exit(1); } // Root Inode (Inode 2) at Block 20 offset 1024 memset(inobuf, 0, 512); dip->di_fileset = htole32(16); dip->di_number = htole32(2); dip->di_nlink = htole32(2); dip->di_mode = htole32(S_IFDIR | 0755); dip->di_size = htole64(1); dip->di_nblocks = htole64(0); dip->di_next_index = htole32(2); dtroot_t *dt = (dtroot_t *)&dip->u._dir._dtroot; dt->header.flag = 0x83; dt->header.nextindex = 0; dt->header.freecnt = 8; dt->header.freelist = 1; dt->header.idotdot = htole32(2); for (int s = 1; s < 8; s++) { dt->slot[s].next = s + 1; } dt->slot[8].next = -1; if (pwrite(fd, inobuf, 512, 20 * BSIZE + 1024) != 512) { printf("[-] Failed to write Root Inode: %s\n", strerror(errno)); exit(1); } // Journal pages 3..1023 (Blocks 1027..2047) struct logpage lp; memset(&lp, 0, sizeof(lp)); for (int i = 3; i < 1024; i++) { lp.h.page = lp.t.page = htole32(i - 3); lp.h.eor = lp.t.eor = htole16(8); if (pwrite(fd, &lp, sizeof(lp), (LOG_START_BLK + i) * BSIZE) != sizeof(lp)) { printf("[-] Failed to write logpage %d: %s\n", i, strerror(errno)); exit(1); } } // Initialize journal superblock & page 2 reset_journal(fd); *out_fd = fd; printf("[+] JFS image created successfully at %s.\n", path); } static volatile int g_mounted = 0; static volatile int g_running = 1; static int g_img_fd = -1; static char g_loop_dev[64]; static void *thread1_mount_umount(void *arg) { (void)arg; PIN_TO_CPU(0); TIMER_START(start); int iter = 0; while (TIMER_NOT_EXPIRED(start, 15.0) && atomic_load(&g_running)) { reset_journal(g_img_fd); int mnt_res = -1; int retry = 0; while (retry < 100 && atomic_load(&g_running)) { mnt_res = mount(g_loop_dev, "/tmp/mnt_jfs", "jfs", 0, NULL); if (mnt_res == 0) break; if (errno == EBUSY) { usleep(500); retry++; continue; } break; } if (mnt_res < 0) { if (!atomic_load(&g_running)) break; printf("[-] Failed to mount %s on /tmp/mnt_jfs: %s\n", g_loop_dev, strerror(errno)); exit(1); } if (iter == 0) { printf("[+] First mount of JFS successful on %s.\n", g_loop_dev); } SIGNAL(&g_mounted, 1); usleep(100 + (rand() % 400)); SIGNAL(&g_mounted, 0); int umnt_res = umount2("/tmp/mnt_jfs", MNT_FORCE | MNT_DETACH); if (umnt_res < 0 && errno != EINVAL) { printf("[-] Failed to umount2 /tmp/mnt_jfs: %s\n", strerror(errno)); exit(1); } iter++; usleep(50); } printf("[+] Thread 1 completed %d mount/umount cycles.\n", iter); return NULL; } static void *thread2_io_mutator(void *arg) { (void)arg; PIN_TO_CPU(1); TIMER_START(start); int count = 0; while (TIMER_NOT_EXPIRED(start, 15.0) && atomic_load(&g_running)) { while (atomic_load(&g_running) && !atomic_load(&g_mounted)) { usleep(50); } if (!atomic_load(&g_running)) break; char file_path[128]; snprintf(file_path, sizeof(file_path), "/tmp/mnt_jfs/file_%d", count++); int fd = open(file_path, O_CREAT | O_RDWR | O_SYNC, 0666); if (fd >= 0) { char data[128]; memset(data, 0x42, sizeof(data)); (void)write(fd, data, sizeof(data)); fdatasync(fd); close(fd); unlink(file_path); } } printf("[+] Thread 2 executed %d transactions.\n", count); return NULL; } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); printf("[+] JFS logmgr Use-After-Free reproducer starting...\n"); const char *img_path = "/dev/shm/jfs_test.img"; create_jfs_image(img_path, &g_img_fd); int ctrl_fd = open("/dev/loop-control", O_RDWR); if (ctrl_fd < 0) { printf("[-] Failed to open /dev/loop-control: %s\n", strerror(errno)); exit(1); } printf("[+] Opened /dev/loop-control successfully.\n"); int loop_idx = ioctl(ctrl_fd, LOOP_CTL_GET_FREE); if (loop_idx < 0) { printf("[-] Failed to get free loop device: %s\n", strerror(errno)); exit(1); } close(ctrl_fd); snprintf(g_loop_dev, sizeof(g_loop_dev), "/dev/loop%d", loop_idx); printf("[+] Allocated free loop device %s.\n", g_loop_dev); int loop_fd = open(g_loop_dev, O_RDWR); if (loop_fd < 0) { printf("[-] Failed to open %s: %s\n", g_loop_dev, strerror(errno)); exit(1); } if (ioctl(loop_fd, LOOP_SET_FD, g_img_fd) < 0) { printf("[-] Failed to LOOP_SET_FD on %s: %s\n", g_loop_dev, strerror(errno)); exit(1); } printf("[+] Attached %s to %s via LOOP_SET_FD.\n", img_path, g_loop_dev); close(loop_fd); printf("[+] Closed loop_fd to allow exclusive mount access.\n"); if (mkdir("/tmp/mnt_jfs", 0755) < 0 && errno != EEXIST) { printf("[-] Failed to mkdir /tmp/mnt_jfs: %s\n", strerror(errno)); exit(1); } printf("[+] Mount directory /tmp/mnt_jfs prepared.\n"); printf("[+] Starting concurrent race threads (Thread 1 on CPU 0, Thread 2 on CPU 1)...\n"); pthread_t th1, th2; if (pthread_create(&th1, NULL, thread1_mount_umount, NULL) != 0) { printf("[-] Failed to create Thread 1: %s\n", strerror(errno)); exit(1); } if (pthread_create(&th2, NULL, thread2_io_mutator, NULL) != 0) { printf("[-] Failed to create Thread 2: %s\n", strerror(errno)); exit(1); } pthread_join(th1, NULL); atomic_store(&g_running, 0); pthread_join(th2, NULL); printf("[+] Worker threads stopped. Allowing async loop operations to drain...\n"); sleep(2); umount2("/tmp/mnt_jfs", MNT_FORCE | MNT_DETACH); loop_fd = open(g_loop_dev, O_RDWR); if (loop_fd >= 0) { if (ioctl(loop_fd, LOOP_CLR_FD, 0) == 0) { printf("[+] Cleared loop device %s.\n", g_loop_dev); } close(loop_fd); } close(g_img_fd); unlink(img_path); rmdir("/tmp/mnt_jfs"); printf("[+] Cleanup complete. Exiting.\n"); return 0; }