// https://syzkaller.appspot.com/bug?id=538dc6e8d36b838c30698f3b085ad9aa3c57cdb8 #define _GNU_SOURCE #include #include #include #include #include #include #include #define NUM_WORKERS 64 #define BURST_FORK 50 #define NESTED_FORK 5 void worker_loop(char *exe_path) { /* Burst forking to maximize the time spent in copy_mm (the read side) */ for (int i = 0; i < BURST_FORK; i++) { pid_t pid = fork(); if (pid == 0) { /* * Nested forking: The child's mm is now in the MGLRU list. * By forking again, it races its own copy_mm against the deletion * of its neighbors when they exit. */ for (int j = 0; j < NESTED_FORK; j++) { pid_t pid2 = fork(); if (pid2 == 0) { exit(0); } } exit(0); } } /* * Re-execute to destroy the old mm (triggering lru_gen_del_mm, the write side) * and allocate a new one, keeping the MGLRU list constantly churning. */ char *args[] = {exe_path, "worker", NULL}; execve("/proc/self/exe", args, NULL); execve(exe_path, args, NULL); printf("[-] execve failed: %s\n", strerror(errno)); exit(1); } int main(int argc, char *argv[]) { setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); /* * Eliminate synchronous waiting: automatically reap children * to maximize concurrency and fork/exit frequency. */ signal(SIGCHLD, SIG_IGN); if (argc > 1 && strcmp(argv[1], "worker") == 0) { worker_loop(argv[0]); return 0; } printf("[*] Starting KCSAN data-race reproducer for copy_mm / lru_gen_del_mm...\n"); pid_t workers[NUM_WORKERS]; for (int i = 0; i < NUM_WORKERS; i++) { pid_t pid = fork(); if (pid < 0) { printf("[-] fork failed: %s\n", strerror(errno)); exit(1); } if (pid == 0) { /* Put worker in its own process group for clean teardown */ setpgid(0, 0); worker_loop(argv[0]); exit(0); } else { workers[i] = pid; } } printf("[+] Spawned %d workers successfully.\n", NUM_WORKERS); /* Let the fork bomb run for a few seconds to trigger the race */ sleep(5); printf("[+] Sleep finished. Cleaning up...\n"); for (int i = 0; i < NUM_WORKERS; i++) { if (workers[i] > 0) { /* Kill the entire process group of the worker */ kill(-workers[i], SIGKILL); } } printf("[+] Reproducer finished.\n"); return 0; }