// https://syzkaller.appspot.com/bug?id=5ad3dc0db2152702bfdc671e6fad58df70304c6b #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Minimal FAT16 boot sector unsigned char boot_sector[512] = { [11] = 0x00, 0x02, // sector_size = 512 [13] = 1, // sec_per_clus = 1 [14] = 1, 0, // reserved = 1 [16] = 1, // fats = 1 [17] = 0x00, 0x02, // dir_entries = 512 [19] = 0x00, 0x20, // sectors = 8192 (4MB) [21] = 0xF8, // media [22] = 32, 0, // fat_length = 32 sectors [24] = 0x20, 0x00, // secs_track = 32 [26] = 0x40, 0x00, // heads = 64 [28] = 0, 0, 0, 0, // hidden [32] = 0, 0, 0, 0, // total_sect [36] = 0x80, // drive_number [37] = 0, // state [38] = 0x29, // signature [39] = 0x12, 0x34, 0x56, 0x78, // vol_id [43] = 'N', 'O', ' ', 'N', 'A', 'M', 'E', ' ', ' ', ' ', ' ', // vol_label [54] = 'F', 'A', 'T', '1', '6', ' ', ' ', ' ', // fs_type [510] = 0x55, [511] = 0xAA // boot signature }; int fd_out; int fd_in; int stop = 0; void *worker(void *arg) { while (!__atomic_load_n(&stop, __ATOMIC_RELAXED)) { off_t in_offset = 0; lseek(fd_out, 0, SEEK_SET); sendfile(fd_out, fd_in, &in_offset, 4096); } return NULL; } int main() { setvbuf(stdout, NULL, _IONBF, 0); printf("[*] Starting reproducer...\n"); int img_fd = open("fat.img", O_CREAT | O_RDWR | O_TRUNC, 0666); if (img_fd < 0) { printf("[-] Failed to create image file: %s\n", strerror(errno)); exit(1); } if (write(img_fd, boot_sector, sizeof(boot_sector)) != sizeof(boot_sector)) { printf("[-] Failed to write boot sector: %s\n", strerror(errno)); exit(1); } if (ftruncate(img_fd, 8192 * 512) < 0) { // 4MB printf("[-] Failed to truncate image file: %s\n", strerror(errno)); exit(1); } close(img_fd); printf("[+] Image file created.\n"); 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); } int loop_num = ioctl(ctrl_fd, LOOP_CTL_GET_FREE); close(ctrl_fd); if (loop_num < 0) { printf("[-] Failed to get free loop device: %s\n", strerror(errno)); exit(1); } printf("[+] Got free loop device: %d\n", loop_num); char loop_name[64]; snprintf(loop_name, sizeof(loop_name), "/dev/loop%d", loop_num); int loop_fd = open(loop_name, O_RDWR); if (loop_fd < 0) { if (mknod(loop_name, S_IFBLK | 0600, makedev(7, loop_num)) < 0) { printf("[-] Failed to mknod %s: %s\n", loop_name, strerror(errno)); exit(1); } loop_fd = open(loop_name, O_RDWR); } if (loop_fd < 0) { printf("[-] Failed to open %s: %s\n", loop_name, strerror(errno)); exit(1); } int file_fd = open("fat.img", O_RDWR); if (file_fd < 0) { printf("[-] Failed to open image file for loop: %s\n", strerror(errno)); exit(1); } if (ioctl(loop_fd, LOOP_SET_FD, file_fd) < 0) { printf("[-] Failed to set loop fd: %s\n", strerror(errno)); exit(1); } close(file_fd); printf("[+] Loop device configured.\n"); // CRITICAL: close loop_fd before mount to avoid EBUSY close(loop_fd); if (mkdir("mnt", 0777) < 0 && errno != EEXIST) { printf("[-] Failed to mkdir mnt: %s\n", strerror(errno)); exit(1); } // Retry mount to handle udev scanning race int retries = 100; int mounted = 0; while (retries--) { if (mount(loop_name, "mnt", "vfat", 0, "") == 0) { mounted = 1; break; } if (errno != EBUSY) { printf("[-] Failed to mount vfat: %s\n", strerror(errno)); exit(1); } usleep(100000); // 100ms } if (!mounted) { printf("[-] Failed to mount vfat after retries: %s\n", strerror(errno)); exit(1); } printf("[+] Mounted vfat successfully.\n"); // O_SYNC is crucial to force generic_write_sync() and trigger the read path fd_out = open("mnt/test", O_CREAT | O_RDWR | O_SYNC, 0666); if (fd_out < 0) { printf("[-] Failed to open mnt/test: %s\n", strerror(errno)); exit(1); } printf("[+] Opened test file with O_SYNC.\n"); fd_in = open("mnt/source", O_CREAT | O_RDWR, 0666); if (fd_in < 0) { printf("[-] Failed to open mnt/source: %s\n", strerror(errno)); exit(1); } char buf[4096]; memset(buf, 'A', sizeof(buf)); if (write(fd_in, buf, sizeof(buf)) != sizeof(buf)) { printf("[-] Failed to write to source file: %s\n", strerror(errno)); exit(1); } printf("[+] Source file created and written.\n"); pthread_t t[4]; for (long i = 0; i < 4; i++) { if (pthread_create(&t[i], NULL, worker, (void *)i) != 0) { printf("[-] Failed to create thread %ld\n", i); exit(1); } } printf("[+] Threads started. Waiting for KCSAN to catch the race...\n"); sleep(10); // Allow KCSAN time to catch the race __atomic_store_n(&stop, 1, __ATOMIC_RELAXED); for (int i = 0; i < 4; i++) { pthread_join(t[i], NULL); } printf("[+] Threads joined.\n"); close(fd_in); close(fd_out); if (umount("mnt") < 0) { printf("[-] Failed to unmount: %s\n", strerror(errno)); } else { printf("[+] Unmounted successfully.\n"); } int loop_fd_clean = open(loop_name, O_RDONLY); if (loop_fd_clean >= 0) { if (ioctl(loop_fd_clean, LOOP_CLR_FD, 0) < 0) { printf("[-] Failed to clear loop fd: %s\n", strerror(errno)); } else { printf("[+] Loop device cleared.\n"); } close(loop_fd_clean); } printf("[+] Reproducer finished.\n"); return 0; }