// https://syzkaller.appspot.com/bug?id=963f2998f909065e173776c92e045d1b210fc935 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef BLKTRACESETUP #define BLKTRACESETUP _IOWR(0x12, 115, struct blk_user_trace_setup) #endif #ifndef BLKTRACESTART #define BLKTRACESTART _IO(0x12, 116) #endif #ifndef BLKTRACESTOP #define BLKTRACESTOP _IO(0x12, 117) #endif #ifndef BLKTRACETEARDOWN #define BLKTRACETEARDOWN _IO(0x12, 118) #endif #define BLKTRACE_BDEV_SIZE 32 struct blk_user_trace_setup { char name[BLKTRACE_BDEV_SIZE]; uint16_t act_mask; uint32_t buf_size; uint32_t buf_nr; uint64_t start_lba; uint64_t end_lba; uint32_t pid; }; #ifndef __NR_clone3 #define __NR_clone3 435 #endif #ifndef CLONE_ARGS_SIZE_VER0 struct clone_args { uint64_t flags; uint64_t pidfd; uint64_t child_tid; uint64_t parent_tid; uint64_t exit_signal; uint64_t stack; uint64_t stack_size; uint64_t tls; uint64_t set_tid; uint64_t set_tid_size; uint64_t cgroup; }; #endif static void write_file(const char *path, const char *val) { int fd = open(path, O_WRONLY); if (fd < 0) { printf("[-] Failed to open %s: %s\n", path, strerror(errno)); return; } ssize_t len = (ssize_t)strlen(val); ssize_t written = write(fd, val, len); if (written < 0) { printf("[-] Failed to write to %s: %s\n", path, strerror(errno)); } else { printf("[+] write to %s successful (%zd bytes).\n", path, written); } close(fd); } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); printf("[+] Starting reproduction for blktrace print_one_line NULL dereference...\n"); /* 1. Check root privileges */ if (geteuid() != 0) { printf("[-] Process must run as root, current euid is %d\n", geteuid()); exit(1); } printf("[+] Running with root privileges (euid=0).\n"); /* 2. Locate tracing directory */ struct stat st; const char *trace_dir = NULL; if (stat("/sys/kernel/tracing", &st) == 0 && S_ISDIR(st.st_mode)) { trace_dir = "/sys/kernel/tracing"; } else if (stat("/sys/kernel/debug/tracing", &st) == 0 && S_ISDIR(st.st_mode)) { trace_dir = "/sys/kernel/debug/tracing"; } else { printf("[-] Tracing directory not found in /sys/kernel/tracing or /sys/kernel/debug/tracing\n"); exit(1); } printf("[+] Found tracing directory: %s\n", trace_dir); /* 3. Setup backing store via memfd */ int memfd = memfd_create("loop_back", 0); if (memfd < 0) { printf("[-] Failed to memfd_create: %s\n", strerror(errno)); exit(1); } printf("[+] memfd_create successful.\n"); int res = ftruncate(memfd, 2 * 1024 * 1024); if (res < 0) { printf("[-] Failed to ftruncate: %s\n", strerror(errno)); close(memfd); exit(1); } printf("[+] ftruncate successful.\n"); /* 4. Configure loop device */ int loop_fd = open("/dev/loop0", O_RDWR); if (loop_fd < 0) { printf("[-] Failed to open /dev/loop0: %s\n", strerror(errno)); close(memfd); exit(1); } printf("[+] open /dev/loop0 successful.\n"); ioctl(loop_fd, LOOP_CLR_FD, 0); res = ioctl(loop_fd, LOOP_SET_FD, memfd); if (res < 0) { printf("[-] Failed to ioctl LOOP_SET_FD: %s\n", strerror(errno)); close(loop_fd); close(memfd); exit(1); } printf("[+] ioctl LOOP_SET_FD successful.\n"); /* 5. Setup blktrace v1 on the queue */ ioctl(loop_fd, BLKTRACESTOP, 0); ioctl(loop_fd, BLKTRACETEARDOWN, 0); struct blk_user_trace_setup buts = { .act_mask = (uint16_t)-1, .buf_size = 512 * 1024, .buf_nr = 4, .start_lba = 0, .end_lba = 0, .pid = 0, }; res = ioctl(loop_fd, BLKTRACESETUP, &buts); if (res < 0) { printf("[-] Failed to ioctl BLKTRACESETUP: %s\n", strerror(errno)); exit(1); } printf("[+] ioctl BLKTRACESETUP successful.\n"); res = ioctl(loop_fd, BLKTRACESTART, 0); if (res < 0) { printf("[-] Failed to ioctl BLKTRACESTART: %s\n", strerror(errno)); exit(1); } printf("[+] ioctl BLKTRACESTART successful.\n"); /* 6. Configure pid_max and ftrace blk tracer */ write_file("/proc/sys/kernel/pid_max", "4194304\n"); char path[256]; snprintf(path, sizeof(path), "%s/trace", trace_dir); int trunc_fd = open(path, O_WRONLY | O_TRUNC); if (trunc_fd >= 0) { printf("[+] Truncated %s.\n", path); close(trunc_fd); } snprintf(path, sizeof(path), "%s/current_tracer", trace_dir); write_file(path, "blk\n"); snprintf(path, sizeof(path), "%s/tracing_on", trace_dir); write_file(path, "1\n"); /* 7. Spawn child process with PID satisfying (pid & 0xfeff) == 14 */ pid_t candidates[] = { 14, 270, 65550, 65806, 131086, 131342, 196622, 196878, 262158, 262414 }; pid_t child = -1; for (size_t i = 0; i < sizeof(candidates) / sizeof(candidates[0]); i++) { pid_t target = candidates[i]; struct clone_args cl_args = { .exit_signal = SIGCHLD, .set_tid = (uint64_t)&target, .set_tid_size = 1, }; printf("[+] Attempting clone3 with target PID %d...\n", target); child = syscall(__NR_clone3, &cl_args, sizeof(cl_args)); if (child >= 0) { printf("[+] clone3 succeeded (child=%d, target=%d).\n", child, target); break; } printf("[-] clone3 with target PID %d failed: %s\n", target, strerror(errno)); /* Fallback via /proc/sys/kernel/ns_last_pid */ char buf[32]; snprintf(buf, sizeof(buf), "%d", target - 1); int pfd = open("/proc/sys/kernel/ns_last_pid", O_WRONLY); if (pfd >= 0) { write(pfd, buf, strlen(buf)); close(pfd); child = fork(); if (child == 0) { break; } if (child > 0) { if ((child & 0xfeff) == 14) { printf("[+] fork via ns_last_pid succeeded with PID %d.\n", child); break; } waitpid(child, NULL, 0); child = -1; } } } if (child < 0) { printf("[-] Failed to spawn child process with target PID.\n"); exit(1); } if (child == 0) { /* Child process context */ pid_t my_pid = getpid(); printf("[+] Child running with PID %d ((pid & 0xfeff) = %d)\n", my_pid, (my_pid & 0xfeff)); char data_buf[4096]; memset(data_buf, 0x41, sizeof(data_buf)); ssize_t pw = pwrite(loop_fd, data_buf, sizeof(data_buf), 0); if (pw < 0) { printf("[-] Child failed to pwrite: %s\n", strerror(errno)); } else { printf("[+] Child pwrite successful (%zd bytes).\n", pw); } ssize_t pr = pread(loop_fd, data_buf, sizeof(data_buf), 0); if (pr < 0) { printf("[-] Child failed to pread: %s\n", strerror(errno)); } else { printf("[+] Child pread successful (%zd bytes).\n", pr); } res = fsync(loop_fd); if (res < 0) { printf("[-] Child failed to fsync: %s\n", strerror(errno)); } else { printf("[+] Child fsync successful.\n"); } /* Submit direct I/O */ void *aligned_buf = NULL; if (posix_memalign(&aligned_buf, 4096, 4096) == 0) { memset(aligned_buf, 0x55, 4096); int dir_fd = open("/dev/loop0", O_RDWR | O_DIRECT); if (dir_fd >= 0) { pwrite(dir_fd, aligned_buf, 4096, 0); pread(dir_fd, aligned_buf, 4096, 0); close(dir_fd); printf("[+] Child direct I/O completed.\n"); } free(aligned_buf); } printf("[+] Child finished I/O, exiting.\n"); _exit(0); } /* Parent waits for child to complete I/O */ int status = 0; res = waitpid(child, &status, 0); if (res < 0) { printf("[-] Failed to waitpid for child %d: %s\n", child, strerror(errno)); exit(1); } printf("[+] Child %d reaped successfully (status=0x%x).\n", child, status); /* 8. Trigger crash by reading trace buffer */ snprintf(path, sizeof(path), "%s/trace", trace_dir); int tr_fd = open(path, O_RDONLY); if (tr_fd < 0) { printf("[-] Failed to open %s: %s\n", path, strerror(errno)); exit(1); } printf("[+] open %s successful.\n", path); printf("[+] Reading %s to trigger print_one_line NULL dereference...\n", path); char read_buf[4096]; ssize_t bytes; while ((bytes = read(tr_fd, read_buf, sizeof(read_buf))) > 0) { printf("[+] Read %zd bytes from trace buffer.\n", bytes); } if (bytes < 0) { printf("[-] read returned error: %s\n", strerror(errno)); } else { printf("[+] Completed reading trace buffer.\n"); } close(tr_fd); close(loop_fd); close(memfd); printf("[+] Reproduction attempt completed.\n"); return 0; }