// https://syzkaller.appspot.com/bug?id=8618ba738345b654ff9283ac8a48fe2801966718 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void bringup_loopback(void) { struct ifreq ifr; int sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { printf("[-] Failed to create socket for ioctl: %s\n", strerror(errno)); exit(1); } memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, "lo", IFNAMSIZ - 1); if (ioctl(sock, SIOCGIFFLAGS, &ifr) < 0) { printf("[-] Failed to ioctl(SIOCGIFFLAGS): %s\n", strerror(errno)); exit(1); } ifr.ifr_flags |= IFF_UP; if (ioctl(sock, SIOCSIFFLAGS, &ifr) < 0) { printf("[-] Failed to ioctl(SIOCSIFFLAGS): %s\n", strerror(errno)); exit(1); } close(sock); printf("[+] Loopback interface brought up.\n"); } void setup_rpcbind_blackhole(void) { int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { printf("[-] Failed to create rpcbind socket: %s\n", strerror(errno)); exit(1); } struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(111); addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); int opt = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { printf("[-] Failed to setsockopt: %s\n", strerror(errno)); exit(1); } if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { printf("[-] Failed to bind rpcbind socket: %s\n", strerror(errno)); exit(1); } if (listen(sock, 10) < 0) { printf("[-] Failed to listen on rpcbind socket: %s\n", strerror(errno)); exit(1); } printf("[+] rpcbind blackhole setup successful.\n"); /* Intentionally do not accept() to blackhole the RPC ping */ } void *thread1_func(void *arg) { printf("[*] Thread 1: opening /tmp/nfsd/portlist\n"); int fd = open("/tmp/nfsd/portlist", O_WRONLY); if (fd < 0) { printf("[-] Thread 1: Failed to open /tmp/nfsd/portlist: %s\n", strerror(errno)); return NULL; } const char *buf = "tcp 2049\n"; printf("[*] Thread 1: writing to /tmp/nfsd/portlist\n"); ssize_t res = syscall(SYS_write, fd, buf, strlen(buf)); if (res < 0) { printf("[-] Thread 1: Failed to write to portlist: %s\n", strerror(errno)); } else { printf("[+] Thread 1: write to portlist returned %zd\n", res); } close(fd); return NULL; } void *thread2_func(void *arg) { printf("[*] Thread 2: opening /tmp/nfsd/threads\n"); int fd = open("/tmp/nfsd/threads", O_WRONLY); if (fd < 0) { printf("[-] Thread 2: Failed to open /tmp/nfsd/threads: %s\n", strerror(errno)); return NULL; } printf("[*] Thread 2: writing to /tmp/nfsd/threads\n"); /* A 0-length write triggers nfsd_nrthreads() */ ssize_t res = syscall(SYS_write, fd, "", 0); if (res < 0) { printf("[-] Thread 2: Failed to write to threads: %s\n", strerror(errno)); } else { printf("[+] Thread 2: write to threads returned %zd\n", res); } close(fd); return NULL; } int main(void) { pthread_t t1, t2; printf("[*] Starting reproducer...\n"); /* Reduce hung task timeout to 10 seconds to trigger within test limits */ int fd = open("/proc/sys/kernel/hung_task_timeout_secs", O_WRONLY); if (fd >= 0) { if (write(fd, "10\n", 3) < 0) { printf("[-] Failed to write hung_task_timeout_secs: %s\n", strerror(errno)); } else { printf("[+] Set hung_task_timeout_secs to 10.\n"); } close(fd); } else { printf("[-] Failed to open hung_task_timeout_secs: %s\n", strerror(errno)); } if (unshare(CLONE_NEWNS | CLONE_NEWNET) < 0) { printf("[-] Failed to unshare: %s\n", strerror(errno)); return 1; } printf("[+] unshare successful.\n"); /* Hide any existing host AF_LOCAL rpcbind sockets */ if (mount("tmpfs", "/run", "tmpfs", 0, NULL) < 0) { printf("[-] Failed to mount tmpfs on /run: %s\n", strerror(errno)); /* non-fatal, might not exist or be accessible */ } else { printf("[+] mount tmpfs on /run successful.\n"); } if (mount("tmpfs", "/var/run", "tmpfs", 0, NULL) < 0) { printf("[-] Failed to mount tmpfs on /var/run: %s\n", strerror(errno)); } else { printf("[+] mount tmpfs on /var/run successful.\n"); } bringup_loopback(); setup_rpcbind_blackhole(); if (mkdir("/tmp/nfsd", 0777) < 0 && errno != EEXIST) { printf("[-] Failed to mkdir /tmp/nfsd: %s\n", strerror(errno)); return 1; } if (mount("nfsd", "/tmp/nfsd", "nfsd", 0, NULL) < 0) { printf("[-] Failed to mount nfsd: %s\n", strerror(errno)); return 1; } printf("[+] mount nfsd successful.\n"); if (pthread_create(&t1, NULL, thread1_func, NULL) != 0) { printf("[-] Failed to create thread 1\n"); return 1; } /* Give Thread 1 time to acquire nfsd_mutex and block in rpc_ping */ sleep(2); if (pthread_create(&t2, NULL, thread2_func, NULL) != 0) { printf("[-] Failed to create thread 2\n"); return 1; } printf("[*] Waiting for hung task detector to fire (30 seconds)...\n"); /* Wait for the hung task detector to fire (now 10 seconds) */ sleep(30); printf("[+] Reproducer finished.\n"); return 0; }