// https://syzkaller.appspot.com/bug?id=d21b88ad6d795080fff1a7c6ba7d1c802c955a33 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Copyright 2026 syzkaller project authors. All rights reserved. // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. // IMPORTANT: Do not copy the macros or definitions below directly into your reproducer. // Instead, add the following line to your reproducer: // #include "race_toolkit.h" // --- Race Condition Toolkit --- // Macros and snippets for CPU pinning, memory barriers, and userfaultfd. #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Unbuffered I/O: Ensure logs are written immediately. #define SETUP_UNBUFFERED_IO() setvbuf(stdout, NULL, _IONBF, 0) // CPU Pinning: Pin the current thread to a specific CPU core. #define PIN_TO_CPU(cpu) \ do { \ cpu_set_t mask; \ CPU_ZERO(&mask); \ CPU_SET(cpu, &mask); \ if (sched_setaffinity(0, sizeof(mask), &mask) == -1) { \ perror("sched_setaffinity"); \ } \ } while (0) // Memory Barrier: Ensure memory ordering. #define MB() __atomic_thread_fence(__ATOMIC_SEQ_CST) // Spin-wait Barrier: Wait until a memory location has a specific value. // Best for tight race windows (low latency, no context switches). #define WAIT_ON(addr, val) \ do { \ while (__atomic_load_n(addr, __ATOMIC_ACQUIRE) != (val)) \ ; \ } while (0) // Signal: Set a memory location to a specific value to release a WAIT_ON. #define SIGNAL(addr, val) __atomic_store_n(addr, val, __ATOMIC_RELEASE) // --- Timing Primitives --- // Robust timing loops in VM environments (using CLOCK_MONOTONIC to avoid time(NULL) jumps). static inline double timer_elapsed_sec(struct timespec* start) { struct timespec now; if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) { perror("clock_gettime(CLOCK_MONOTONIC) elapsed"); exit(1); } return (double)(now.tv_sec - start->tv_sec) + (double)(now.tv_nsec - start->tv_nsec) / 1e9; } // Initialize a monotonic timer variable. #define TIMER_START(t) \ struct timespec t; \ if (clock_gettime(CLOCK_MONOTONIC, &t) == -1) { \ perror("clock_gettime(CLOCK_MONOTONIC) start"); \ exit(1); \ } // Check if the elapsed time since 't' is less than 'sec' seconds. #define TIMER_NOT_EXPIRED(t, sec) (timer_elapsed_sec(&(t)) < (double)(sec)) // Futex-based Event: Shared with syzkaller executor. // Best for general synchronization or longer waits to save CPU. typedef struct { int state; } event_t; static void event_init(event_t* ev) { ev->state = 0; } static void event_reset(event_t* ev) { ev->state = 0; } static void event_set(event_t* ev) { if (__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) { fprintf(stderr, "event already set\n"); exit(1); } __atomic_store_n(&ev->state, 1, __ATOMIC_RELEASE); syscall(SYS_futex, &ev->state, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1000000); } static void event_wait(event_t* ev) { while (!__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) syscall(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, 0); } // userfaultfd setup: Register a memory range for page fault handling. static int setup_uffd(void* addr, size_t len) { int uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); if (uffd == -1) return -1; struct uffdio_api api = {.api = UFFD_API, .features = 0}; if (ioctl(uffd, UFFDIO_API, &api) == -1) { close(uffd); return -1; } struct uffdio_register reg = { .range = {.start = (uintptr_t)addr, .len = len}, .mode = UFFDIO_REGISTER_MODE_MISSING}; if (ioctl(uffd, UFFDIO_REGISTER, ®) == -1) { close(uffd); return -1; } return uffd; } // --- Guidance on Usage --- // 1. Use WAIT_ON/SIGNAL for tight race conditions to avoid scheduling overhead. // 2. Use event_t (futexes) for general coordination or when waiting for longer periods. // 3. Always use PIN_TO_CPU to increase race probability on multi-core systems. // 4. Use setup_uffd to register a memory range for page fault handling. This allows you to // pause a thread accessing that memory until you handle the fault, creating a reliable // and controllable race window. // 5. Call SETUP_UNBUFFERED_IO() at the start of main() to ensure that logs are printed // immediately. This is essential for understanding the exact interleaving of events // when debugging race conditions. // 6. For timing-based loops (e.g., running a race for 10 seconds), do NOT use time(NULL) // or loops relying on real-time clocks, as VM clocks are highly unreliable and can fail or drift. // Instead, use the robust monotonic timing primitives TIMER_START and TIMER_NOT_EXPIRED: // TIMER_START(start); // while (TIMER_NOT_EXPIRED(start, 10.0)) { // // Your race logic here // } #ifndef LOOP_CTL_GET_FREE #define LOOP_CTL_GET_FREE 0x4C82 #endif #ifndef LOOP_SET_FD #define LOOP_SET_FD 0x4C00 #endif #ifndef LOOP_SET_BLOCK_SIZE #define LOOP_SET_BLOCK_SIZE 0x4C09 #endif #ifndef BLKFLSBUF #define BLKFLSBUF _IO(0x12,97) #endif #pragma pack(push, 1) struct MFT_REF { unsigned int low; unsigned short high; unsigned short seq; }; struct NTFS_RECORD_HEADER { unsigned int sign; unsigned short fix_off; unsigned short fix_num; unsigned long long lsn; }; struct MFT_REC { struct NTFS_RECORD_HEADER rhdr; unsigned short seq; unsigned short hard_links; unsigned short attr_off; unsigned short flags; unsigned int used; unsigned int total; struct MFT_REF parent_ref; unsigned short next_attr_id; unsigned short res; unsigned int mft_record; unsigned short fixups[3]; }; struct ATTR_RESIDENT { unsigned int data_size; unsigned short data_off; unsigned char flags; unsigned char res; }; struct ATTR_NONRESIDENT { unsigned long long svcn; unsigned long long evcn; unsigned short run_off; unsigned short c_unit; unsigned int res; unsigned long long alloc_size; unsigned long long data_size; unsigned long long valid_size; }; struct ATTRIB { unsigned int type; unsigned int size; unsigned char non_res; unsigned char name_len; unsigned short name_off; unsigned short flags; unsigned short id; union { struct ATTR_RESIDENT res; struct ATTR_NONRESIDENT nres; }; }; struct ATTR_STD_INFO5 { unsigned long long cr_time; unsigned long long m_time; unsigned long long c_time; unsigned long long a_time; unsigned int fa; unsigned int max_ver_num; unsigned int ver_num; unsigned int class_id; unsigned int owner_id; unsigned int security_id; unsigned long long quota_charge; unsigned long long usn; }; struct NTFS_DUP_INFO { unsigned long long cr_time; unsigned long long m_time; unsigned long long c_time; unsigned long long a_time; unsigned long long alloc_size; unsigned long long data_size; unsigned int fa; unsigned int extend_data; }; struct ATTR_FILE_NAME { struct MFT_REF home; struct NTFS_DUP_INFO dup; unsigned char name_len; unsigned char type; unsigned short name[1]; }; struct VOLUME_INFO { unsigned long long res1; unsigned char major_ver; unsigned char minor_ver; unsigned short flags; }; struct ATTR_DEF_ENTRY { unsigned short name[0x40]; unsigned int type; unsigned int res; unsigned int rule; unsigned int flags; unsigned long long min_sz; unsigned long long max_sz; }; struct INDEX_HDR { unsigned int de_off; unsigned int used; unsigned int total; unsigned int flags; }; struct INDEX_ROOT { unsigned int type; unsigned int rule; unsigned int index_block_size; unsigned char index_block_clst; unsigned char res[3]; struct INDEX_HDR ihdr; }; struct NTFS_DE { union { struct MFT_REF ref; struct { unsigned short data_off; unsigned short data_size; unsigned int res; } view; }; unsigned short size; unsigned short key_size; unsigned short flags; unsigned short res; }; struct NTFS_BOOT { unsigned char jump_code[3]; unsigned char system_id[8]; unsigned char bytes_per_sector[2]; unsigned char sectors_per_clusters; unsigned char unused1[7]; unsigned char media_type; unsigned char unused2[2]; unsigned short sct_per_track; unsigned short heads; unsigned int hidden_sectors; unsigned char unused3[4]; unsigned char bios_drive_num; unsigned char unused4; unsigned char signature_ex; unsigned char unused5; unsigned long long sectors_per_volume; unsigned long long mft_clst; unsigned long long mft2_clst; signed char record_size; unsigned char unused6[3]; signed char index_size; unsigned char unused7[3]; unsigned long long serial_num; unsigned int check_sum; unsigned char boot_code[0x200 - 0x50 - 2 - 4]; unsigned char boot_magic[2]; }; #pragma pack(pop) static inline unsigned short cpu_to_le16(unsigned short val) { #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ return __builtin_bswap16(val); #else return val; #endif } static inline unsigned int cpu_to_le32(unsigned int val) { #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ return __builtin_bswap32(val); #else return val; #endif } static inline unsigned long long cpu_to_le64(unsigned long long val) { #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ return __builtin_bswap64(val); #else return val; #endif } static void str_to_utf16le(const char *src, unsigned short *dst, int max_len) { for (int i = 0; i < max_len; i++) { dst[i] = cpu_to_le16((unsigned char)src[i]); if (src[i] == '\0') break; } } static unsigned int write_resident_attr(unsigned char *rec, unsigned int off, unsigned int type, const void *data, unsigned int data_len, const unsigned short *name, unsigned int name_len) { struct ATTRIB *attr = (struct ATTRIB *)(rec + off); attr->type = cpu_to_le32(type); attr->non_res = 0; attr->flags = 0; attr->id = cpu_to_le16(0); unsigned int cur_off = 0x18; if (name_len > 0) { attr->name_len = name_len; attr->name_off = cpu_to_le16(cur_off); memcpy(rec + off + cur_off, name, name_len * 2); cur_off = (cur_off + name_len * 2 + 7) & ~7; } else { attr->name_len = 0; attr->name_off = 0; } attr->res.data_size = cpu_to_le32(data_len); attr->res.data_off = cpu_to_le16(cur_off); attr->res.flags = 0; attr->res.res = 0; if (data_len > 0 && data != NULL) { memcpy(rec + off + cur_off, data, data_len); } unsigned int total_size = (cur_off + data_len + 7) & ~7; attr->size = cpu_to_le32(total_size); return off + total_size; } static unsigned int write_nonresident_attr(unsigned char *rec, unsigned int off, unsigned int type, unsigned long long svcn, unsigned long long evcn, unsigned long long alloc_size, unsigned long long data_size, unsigned long long valid_size, const unsigned char *run_list, unsigned int run_list_len, const unsigned short *name, unsigned int name_len) { struct ATTRIB *attr = (struct ATTRIB *)(rec + off); attr->type = cpu_to_le32(type); attr->non_res = 1; attr->flags = 0; attr->id = cpu_to_le16(0); unsigned int cur_off = 0x40; if (name_len > 0) { attr->name_len = name_len; attr->name_off = cpu_to_le16(cur_off); memcpy(rec + off + cur_off, name, name_len * 2); cur_off = (cur_off + name_len * 2 + 7) & ~7; } else { attr->name_len = 0; attr->name_off = 0; } attr->nres.svcn = cpu_to_le64(svcn); attr->nres.evcn = cpu_to_le64(evcn); attr->nres.run_off = cpu_to_le16(cur_off); attr->nres.c_unit = 0; attr->nres.res = 0; attr->nres.alloc_size = cpu_to_le64(alloc_size); attr->nres.data_size = cpu_to_le64(data_size); attr->nres.valid_size = cpu_to_le64(valid_size); memcpy(rec + off + cur_off, run_list, run_list_len); unsigned int total_size = (cur_off + run_list_len + 7) & ~7; attr->size = cpu_to_le32(total_size); return off + total_size; } static void init_mft_record(unsigned char *rec, unsigned int rno, unsigned int flags) { memset(rec, 0, 1024); struct MFT_REC *mrec = (struct MFT_REC *)rec; mrec->rhdr.sign = cpu_to_le32(0x454c4946); /* "FILE" */ mrec->rhdr.fix_off = cpu_to_le16(0x30); mrec->rhdr.fix_num = cpu_to_le16(3); mrec->seq = cpu_to_le16(rno == 0 ? 1 : rno); mrec->hard_links = cpu_to_le16(1); mrec->attr_off = cpu_to_le16(0x38); mrec->flags = cpu_to_le16(flags); mrec->total = cpu_to_le32(1024); mrec->mft_record = cpu_to_le32(rno); mrec->fixups[0] = cpu_to_le16(1); *(unsigned short *)(rec + 510) = cpu_to_le16(1); *(unsigned short *)(rec + 1022) = cpu_to_le16(1); } static void finalize_mft_record(unsigned char *rec, unsigned int off) { *(unsigned int *)(rec + off) = cpu_to_le32(0xFFFFFFFF); /* ATTR_END */ struct MFT_REC *mrec = (struct MFT_REC *)rec; mrec->used = cpu_to_le32(off + 8); } void generate_ntfs_image(unsigned char *buf) { memset(buf, 0, 16 * 1048576); /* 1. Write Boot Sector (Sector 0) */ struct NTFS_BOOT *boot = (struct NTFS_BOOT *)buf; boot->jump_code[0] = 0xEB; boot->jump_code[1] = 0x52; boot->jump_code[2] = 0x90; memcpy(boot->system_id, "NTFS ", 8); boot->bytes_per_sector[0] = 0x00; boot->bytes_per_sector[1] = 0x10; /* 4096 */ boot->sectors_per_clusters = 1; /* 4096 */ boot->media_type = 0xF8; boot->sct_per_track = cpu_to_le16(63); boot->heads = cpu_to_le16(255); boot->sectors_per_volume = cpu_to_le64(4095); /* 16MB - 4096 */ boot->mft_clst = cpu_to_le64(4); boot->mft2_clst = cpu_to_le64(2); boot->record_size = -10; /* 1024 */ boot->index_size = -12; /* 4096 */ boot->serial_num = cpu_to_le64(0x1234567890ABCDEFULL); boot->boot_magic[0] = 0x55; boot->boot_magic[1] = 0xAA; /* Copy Boot Sector to the last sector of the volume */ memcpy(buf + 16 * 1048576 - 4096, buf, 4096); /* 2. Fill $LogFile with 0xFF to trigger automatic initialization by ntfs3 */ memset(buf + 20 * 4096, 0xFF, 262144); /* 3. Populate $UpCase Table (Clusters 84 to 115) */ unsigned short *upcase = (unsigned short *)(buf + 84 * 4096); for (int i = 0; i < 65536; i++) { if (i >= 'a' && i <= 'z') { upcase[i] = cpu_to_le16(i - 32); } else { upcase[i] = cpu_to_le16(i); } } /* 4. Populate MFT Records (starting at Cluster 4, offset 16384) */ unsigned char *mft = buf + 16384; unsigned char *rec; unsigned int off; struct ATTR_STD_INFO5 std = {0}; std.fa = 0x06; /* HIDDEN | SYSTEM */ struct ATTR_FILE_NAME fname = {0}; fname.home.low = cpu_to_le32(5); fname.home.seq = cpu_to_le16(5); fname.dup.fa = 0x06; fname.name_len = 4; fname.type = 3; unsigned char fname_buf[128]; /* Record 0: $MFT */ rec = mft + 0 * 1024; init_mft_record(rec, 0, 1); off = 0x38; off = write_resident_attr(rec, off, 0x10, &std, sizeof(std), NULL, 0); unsigned short name_mft[4]; str_to_utf16le("$MFT", name_mft, 4); fname.name_len = 4; memcpy(fname_buf, &fname, 0x42); memcpy(fname_buf + 0x42, name_mft, 8); off = write_resident_attr(rec, off, 0x30, fname_buf, 0x42 + 8, NULL, 0); unsigned char run_mft[] = { 0x11, 0x10, 0x04, 0x00 }; /* 16 clusters at LCN 4 */ off = write_nonresident_attr(rec, off, 0x80, 0, 15, 65536, 65536, 65536, run_mft, sizeof(run_mft), NULL, 0); unsigned char run_mft_bmp[] = { 0x11, 0x01, 0x75, 0x00 }; /* 1 cluster at LCN 117 */ off = write_nonresident_attr(rec, off, 0xB0, 0, 0, 4096, 4096, 4096, run_mft_bmp, sizeof(run_mft_bmp), NULL, 0); finalize_mft_record(rec, off); /* Record 1: $MFTMirr */ rec = mft + 1 * 1024; init_mft_record(rec, 1, 1); off = 0x38; off = write_resident_attr(rec, off, 0x10, &std, sizeof(std), NULL, 0); unsigned short name_mirr[8]; str_to_utf16le("$MFTMirr", name_mirr, 8); fname.name_len = 8; memcpy(fname_buf, &fname, 0x42); memcpy(fname_buf + 0x42, name_mirr, 16); off = write_resident_attr(rec, off, 0x30, fname_buf, 0x42 + 16, NULL, 0); unsigned char run_mirr[] = { 0x11, 0x01, 0x02, 0x00 }; /* 1 cluster at LCN 2 */ off = write_nonresident_attr(rec, off, 0x80, 0, 0, 4096, 4096, 4096, run_mirr, sizeof(run_mirr), NULL, 0); finalize_mft_record(rec, off); /* Record 2: $LogFile */ rec = mft + 2 * 1024; init_mft_record(rec, 2, 1); off = 0x38; off = write_resident_attr(rec, off, 0x10, &std, sizeof(std), NULL, 0); unsigned short name_log[8]; str_to_utf16le("$LogFile", name_log, 8); fname.name_len = 8; memcpy(fname_buf, &fname, 0x42); memcpy(fname_buf + 0x42, name_log, 16); off = write_resident_attr(rec, off, 0x30, fname_buf, 0x42 + 16, NULL, 0); unsigned char run_log[] = { 0x11, 0x40, 0x14, 0x00 }; /* 64 clusters at LCN 20 */ off = write_nonresident_attr(rec, off, 0x80, 0, 63, 262144, 262144, 262144, run_log, sizeof(run_log), NULL, 0); finalize_mft_record(rec, off); /* Record 3: $Volume */ rec = mft + 3 * 1024; init_mft_record(rec, 3, 1); off = 0x38; off = write_resident_attr(rec, off, 0x10, &std, sizeof(std), NULL, 0); unsigned short name_vol[7]; str_to_utf16le("$Volume", name_vol, 7); fname.name_len = 7; memcpy(fname_buf, &fname, 0x42); memcpy(fname_buf + 0x42, name_vol, 14); off = write_resident_attr(rec, off, 0x30, fname_buf, 0x42 + 14, NULL, 0); struct VOLUME_INFO vinfo = {0}; vinfo.major_ver = 2; /* Bypass $Secure initialization */ vinfo.minor_ver = 1; off = write_resident_attr(rec, off, 0x70, &vinfo, sizeof(vinfo), NULL, 0); off = write_resident_attr(rec, off, 0x80, NULL, 0, NULL, 0); finalize_mft_record(rec, off); /* Record 4: $AttrDef */ rec = mft + 4 * 1024; init_mft_record(rec, 4, 1); off = 0x38; off = write_resident_attr(rec, off, 0x10, &std, sizeof(std), NULL, 0); unsigned short name_adef[8]; str_to_utf16le("$AttrDef", name_adef, 8); fname.name_len = 8; memcpy(fname_buf, &fname, 0x42); memcpy(fname_buf + 0x42, name_adef, 16); off = write_resident_attr(rec, off, 0x30, fname_buf, 0x42 + 16, NULL, 0); struct ATTR_DEF_ENTRY adef[2] = {0}; unsigned short adef_name[21]; str_to_utf16le("$STANDARD_INFORMATION", adef_name, 21); memcpy(adef[0].name, adef_name, 42); adef[0].type = cpu_to_le32(0x10); /* ATTR_STD */ unsigned short adef_ea_name[4]; str_to_utf16le("$EA", adef_ea_name, 4); memcpy(adef[1].name, adef_ea_name, 8); adef[1].type = cpu_to_le32(0xE0); /* ATTR_EA */ adef[1].max_sz = cpu_to_le64(0x10000); /* 64KiB */ off = write_resident_attr(rec, off, 0x80, adef, sizeof(adef), NULL, 0); finalize_mft_record(rec, off); /* Record 5: Root Directory . */ rec = mft + 5 * 1024; init_mft_record(rec, 5, 3); /* IN_USE | DIRECTORY */ off = 0x38; struct ATTR_STD_INFO5 std_dir = {0}; std_dir.fa = 0x10; /* DIRECTORY */ off = write_resident_attr(rec, off, 0x10, &std_dir, sizeof(std_dir), NULL, 0); unsigned short name_root[1]; str_to_utf16le(".", name_root, 1); fname.dup.fa = 0x10; fname.name_len = 1; memcpy(fname_buf, &fname, 0x42); memcpy(fname_buf + 0x42, name_root, 2); off = write_resident_attr(rec, off, 0x30, fname_buf, 0x42 + 2, NULL, 0); unsigned char iroot_buf[128] = {0}; struct INDEX_ROOT *iroot = (struct INDEX_ROOT *)iroot_buf; iroot->type = 0x30; iroot->rule = 1; iroot->index_block_size = cpu_to_le32(4096); iroot->index_block_clst = 1; iroot->ihdr.de_off = cpu_to_le32(16); iroot->ihdr.used = cpu_to_le32(32); iroot->ihdr.total = cpu_to_le32(32); struct NTFS_DE *de = (struct NTFS_DE *)(iroot_buf + 32); de->size = cpu_to_le16(16); de->flags = 0x02; /* LAST */ unsigned short name_i30[4]; str_to_utf16le("$I30", name_i30, 4); off = write_resident_attr(rec, off, 0x90, iroot_buf, 48, name_i30, 4); finalize_mft_record(rec, off); /* Record 6: $Bitmap */ rec = mft + 6 * 1024; init_mft_record(rec, 6, 1); off = 0x38; off = write_resident_attr(rec, off, 0x10, &std, sizeof(std), NULL, 0); unsigned short name_bmp[7]; str_to_utf16le("$Bitmap", name_bmp, 7); fname.dup.fa = 0x06; fname.name_len = 7; memcpy(fname_buf, &fname, 0x42); memcpy(fname_buf + 0x42, name_bmp, 14); off = write_resident_attr(rec, off, 0x30, fname_buf, 0x42 + 14, NULL, 0); unsigned char run_bmp[] = { 0x11, 0x01, 0x74, 0x00 }; /* 1 cluster at LCN 116 */ off = write_nonresident_attr(rec, off, 0x80, 0, 0, 4096, 4096, 4096, run_bmp, sizeof(run_bmp), NULL, 0); finalize_mft_record(rec, off); /* Record 7: $Boot */ rec = mft + 7 * 1024; init_mft_record(rec, 7, 1); off = 0x38; off = write_resident_attr(rec, off, 0x10, &std, sizeof(std), NULL, 0); unsigned short name_boot[5]; str_to_utf16le("$Boot", name_boot, 5); fname.name_len = 5; memcpy(fname_buf, &fname, 0x42); memcpy(fname_buf + 0x42, name_boot, 10); off = write_resident_attr(rec, off, 0x30, fname_buf, 0x42 + 10, NULL, 0); off = write_resident_attr(rec, off, 0x80, NULL, 0, NULL, 0); finalize_mft_record(rec, off); /* Record 8: $BadClus */ rec = mft + 8 * 1024; init_mft_record(rec, 8, 1); off = 0x38; off = write_resident_attr(rec, off, 0x10, &std, sizeof(std), NULL, 0); unsigned short name_bad[8]; str_to_utf16le("$BadClus", name_bad, 8); fname.name_len = 8; memcpy(fname_buf, &fname, 0x42); memcpy(fname_buf + 0x42, name_bad, 16); off = write_resident_attr(rec, off, 0x30, fname_buf, 0x42 + 16, NULL, 0); unsigned char run_bad[] = { 0x00 }; /* Empty runlist */ off = write_nonresident_attr(rec, off, 0x80, 0, 0, 0, 0, 0, run_bad, sizeof(run_bad), NULL, 0); finalize_mft_record(rec, off); /* Record 9: $Secure */ rec = mft + 9 * 1024; init_mft_record(rec, 9, 1); off = 0x38; off = write_resident_attr(rec, off, 0x10, &std, sizeof(std), NULL, 0); unsigned short name_sec[7]; str_to_utf16le("$Secure", name_sec, 7); fname.name_len = 7; memcpy(fname_buf, &fname, 0x42); memcpy(fname_buf + 0x42, name_sec, 14); off = write_resident_attr(rec, off, 0x30, fname_buf, 0x42 + 14, NULL, 0); unsigned char sdh_buf[128] = {0}; struct INDEX_ROOT *sdh = (struct INDEX_ROOT *)sdh_buf; sdh->type = 0; sdh->rule = cpu_to_le32(0x12); sdh->index_block_size = cpu_to_le32(4096); sdh->index_block_clst = 1; sdh->ihdr.de_off = cpu_to_le32(16); sdh->ihdr.used = cpu_to_le32(32); sdh->ihdr.total = cpu_to_le32(32); struct NTFS_DE *de_sdh = (struct NTFS_DE *)(sdh_buf + 32); de_sdh->size = cpu_to_le16(16); de_sdh->flags = 0x02; unsigned short name_sdh[4]; str_to_utf16le("$SDH", name_sdh, 4); off = write_resident_attr(rec, off, 0x90, sdh_buf, 48, name_sdh, 4); unsigned char sii_buf[128] = {0}; struct INDEX_ROOT *sii = (struct INDEX_ROOT *)sii_buf; sii->type = 0; sii->rule = cpu_to_le32(0x10); sii->index_block_size = cpu_to_le32(4096); sii->index_block_clst = 1; sii->ihdr.de_off = cpu_to_le32(16); sii->ihdr.used = cpu_to_le32(32); sii->ihdr.total = cpu_to_le32(32); struct NTFS_DE *de_sii = (struct NTFS_DE *)(sii_buf + 32); de_sii->size = cpu_to_le16(16); de_sii->flags = 0x02; unsigned short name_sii[4]; str_to_utf16le("$SII", name_sii, 4); off = write_resident_attr(rec, off, 0x90, sii_buf, 48, name_sii, 4); unsigned char run_sds[] = { 0x01, 0x40, 0x00 }; /* Sparse run of 64 clusters */ unsigned short name_sds[4]; str_to_utf16le("$SDS", name_sds, 4); off = write_nonresident_attr(rec, off, 0x80, 0, 63, 262144, 262144, 262144, run_sds, sizeof(run_sds), name_sds, 4); finalize_mft_record(rec, off); /* Record 10: $UpCase */ rec = mft + 10 * 1024; init_mft_record(rec, 10, 1); off = 0x38; off = write_resident_attr(rec, off, 0x10, &std, sizeof(std), NULL, 0); unsigned short name_upc[7]; str_to_utf16le("$UpCase", name_upc, 7); fname.name_len = 7; memcpy(fname_buf, &fname, 0x42); memcpy(fname_buf + 0x42, name_upc, 14); off = write_resident_attr(rec, off, 0x30, fname_buf, 0x42 + 14, NULL, 0); unsigned char run_upcase[] = { 0x11, 0x20, 0x54, 0x00 }; /* 32 clusters at LCN 84 */ off = write_nonresident_attr(rec, off, 0x80, 0, 31, 131072, 131072, 131072, run_upcase, sizeof(run_upcase), NULL, 0); finalize_mft_record(rec, off); /* Record 11: $Extend */ rec = mft + 11 * 1024; init_mft_record(rec, 11, 3); off = 0x38; off = write_resident_attr(rec, off, 0x10, &std_dir, sizeof(std_dir), NULL, 0); unsigned short name_ext[7]; str_to_utf16le("$Extend", name_ext, 7); fname.dup.fa = 0x10; fname.name_len = 7; memcpy(fname_buf, &fname, 0x42); memcpy(fname_buf + 0x42, name_ext, 14); off = write_resident_attr(rec, off, 0x30, fname_buf, 0x42 + 14, NULL, 0); off = write_resident_attr(rec, off, 0x90, iroot_buf, 48, name_i30, 4); finalize_mft_record(rec, off); /* 5. Mirror the first 4 MFT records to $MFTMirr (Cluster 2, offset 8192) */ memcpy(buf + 8192, mft, 4096); /* 6. Populate MFT Allocation Bitmap (Cluster 117, offset 479232) */ buf[479232] = 0xFF; buf[479233] = 0x0F; /* First 12 records allocated */ /* 7. Populate Volume Allocation Bitmap (Cluster 116, offset 475136) */ unsigned char *vol_bmp = buf + 475136; #define SET_BIT(bmp, bit) (bmp[(bit) / 8] |= (1 << ((bit) % 8))) SET_BIT(vol_bmp, 0); /* Boot Sector */ SET_BIT(vol_bmp, 2); /* $MFTMirr */ for (int i = 4; i <= 19; i++) SET_BIT(vol_bmp, i); /* $MFT */ for (int i = 20; i <= 83; i++) SET_BIT(vol_bmp, i); /* $LogFile */ for (int i = 84; i <= 115; i++) SET_BIT(vol_bmp, i); /* $UpCase */ SET_BIT(vol_bmp, 116); /* Volume Bitmap */ SET_BIT(vol_bmp, 117); /* MFT Bitmap */ } #define NUM_WRITERS 8 #define NUM_MOUNTINFO 8 #define NUM_REMOUNTERS 4 int fds[NUM_WRITERS]; volatile int stop = 0; char loop_dev[64]; int valid_cpus[1024]; int num_cpus = 0; void *writer_thread(void *arg) { int id = (int)(long)arg; if (num_cpus > 0) { int cpu = valid_cpus[id % num_cpus]; PIN_TO_CPU(cpu); } int fd = fds[id]; // Force non-resident if (ftruncate(fd, 65536) < 0) { perror("ftruncate 65536"); } if (ftruncate(fd, 1) < 0) { perror("ftruncate 1"); } while (!stop) { ftruncate(fd, 1); ftruncate(fd, 2); } return NULL; } void *mountinfo_thread(void *arg) { int id = (int)(long)arg; if (num_cpus > 0) { int cpu = valid_cpus[(id + NUM_WRITERS) % num_cpus]; PIN_TO_CPU(cpu); } char buf[4096]; while (!stop) { int fd = open("/proc/self/mountinfo", O_RDONLY); if (fd >= 0) { while (read(fd, buf, sizeof(buf)) > 0) {} close(fd); } } return NULL; } void *remount_thread(void *arg) { int id = (int)(long)arg; if (num_cpus > 0) { int cpu = valid_cpus[(id + NUM_WRITERS + NUM_MOUNTINFO) % num_cpus]; PIN_TO_CPU(cpu); } while (!stop) { mount(loop_dev, "/mnt", "ntfs3", MS_REMOUNT, "uid=0"); mount(loop_dev, "/mnt", "ntfs3", MS_REMOUNT, "uid=1"); } return NULL; } int setup_loop_device(const char *file, char *loop_dev) { int loop_ctl_fd = open("/dev/loop-control", O_RDWR); if (loop_ctl_fd < 0) { perror("open /dev/loop-control"); return -1; } int dev_nr = ioctl(loop_ctl_fd, LOOP_CTL_GET_FREE); close(loop_ctl_fd); if (dev_nr < 0) { perror("ioctl LOOP_CTL_GET_FREE"); return -1; } sprintf(loop_dev, "/dev/loop%d", dev_nr); int loop_fd = open(loop_dev, O_RDWR); if (loop_fd < 0) { perror("open loop device"); return -1; } int file_fd = open(file, O_RDWR); if (file_fd < 0) { perror("open image file"); close(loop_fd); return -1; } if (ioctl(loop_fd, LOOP_SET_FD, file_fd) < 0) { perror("ioctl LOOP_SET_FD"); close(loop_fd); close(file_fd); return -1; } ioctl(loop_fd, LOOP_SET_BLOCK_SIZE, 4096); ioctl(loop_fd, BLKFLSBUF, 0); close(loop_fd); close(file_fd); return 0; } int main() { SETUP_UNBUFFERED_IO(); cpu_set_t cpuset; CPU_ZERO(&cpuset); if (sched_getaffinity(0, sizeof(cpuset), &cpuset) == 0) { for (int i = 0; i < 1024; i++) { if (CPU_ISSET(i, &cpuset)) { valid_cpus[num_cpus++] = i; } } } printf("[*] Generating minimal NTFS image...\n"); unsigned char *buf = malloc(16 * 1048576); if (!buf) { perror("malloc"); return 1; } generate_ntfs_image(buf); int file_fd = open("/tmp/img", O_RDWR | O_CREAT | O_TRUNC, 0666); if (file_fd < 0) { perror("open /tmp/img"); return 1; } if (write(file_fd, buf, 16 * 1048576) != 16 * 1048576) { perror("write image"); return 1; } fsync(file_fd); close(file_fd); free(buf); sync(); printf("[+] Image generated successfully.\n"); mkdir("/mnt", 0777); if (setup_loop_device("/tmp/img", loop_dev) != 0) { fprintf(stderr, "[-] Failed to setup loop device\n"); return 1; } printf("[+] Loop device %s setup successfully.\n", loop_dev); if (mount(loop_dev, "/mnt", "ntfs3", 0, "") != 0) { perror("mount"); return 1; } printf("[+] Mounted successfully.\n"); for (int i = 0; i < NUM_WRITERS; i++) { char path[64]; sprintf(path, "/mnt/test%d", i); fds[i] = open(path, O_RDWR | O_CREAT, 0666); if (fds[i] < 0) { perror("open target file"); return 1; } } printf("[+] Target files opened successfully.\n"); pthread_t writers[NUM_WRITERS]; pthread_t mountinfos[NUM_MOUNTINFO]; pthread_t remounters[NUM_REMOUNTERS]; for (long i = 0; i < NUM_WRITERS; i++) { pthread_create(&writers[i], NULL, writer_thread, (void *)i); } for (long i = 0; i < NUM_MOUNTINFO; i++) { pthread_create(&mountinfos[i], NULL, mountinfo_thread, (void *)i); } for (long i = 0; i < NUM_REMOUNTERS; i++) { pthread_create(&remounters[i], NULL, remount_thread, (void *)i); } printf("[*] Racing for 20 seconds...\n"); TIMER_START(start); while (TIMER_NOT_EXPIRED(start, 20.0)) { usleep(100000); } stop = 1; for (int i = 0; i < NUM_WRITERS; i++) { pthread_join(writers[i], NULL); close(fds[i]); } for (int i = 0; i < NUM_MOUNTINFO; i++) { pthread_join(mountinfos[i], NULL); } for (int i = 0; i < NUM_REMOUNTERS; i++) { pthread_join(remounters[i], NULL); } umount("/mnt"); printf("[+] Done.\n"); return 0; }