// https://syzkaller.appspot.com/bug?id=3028fb37e9c0f5ec726728e3e706d2d0bec2d13f #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CB_PORT 45454 #define DEFAULT_NFS_PORT 2049 #define SYSCTL_PATH "/proc/sys/fs/nfs/nfs_callback_tcpport" #define MNT_DIR "./mnt_repro" static void handle_client(int cfd) { while (1) { uint32_t req[64]; ssize_t n = recv(cfd, req, sizeof(req), 0); if (n < 8) break; /* SunRPC NULL ping reply: * resp[0]: fragment header (0x80000000 | 24 bytes payload = 0x80000018) * resp[1]: client XID (echoed back) * resp[2]: RPC_REPLY (1) * resp[3]: RPC_MSG_ACCEPTED (0) * resp[4]: RPC_AUTH_NULL (0) * resp[5]: verifier length (0) * resp[6]: RPC_SUCCESS (0) */ uint32_t resp[7]; resp[0] = htonl(0x80000018); resp[1] = req[1]; resp[2] = htonl(1); resp[3] = htonl(0); resp[4] = htonl(0); resp[5] = htonl(0); resp[6] = htonl(0); if (send(cfd, resp, sizeof(resp), 0) < 0) break; } close(cfd); exit(0); } static void run_nfs_server(int sfd) { signal(SIGPIPE, SIG_IGN); while (1) { int cfd = accept(sfd, NULL, NULL); if (cfd < 0) { if (errno == EINTR) continue; break; } pid_t cpid = fork(); if (cpid == 0) { close(sfd); handle_client(cfd); } close(cfd); } exit(0); } int main(void) { setvbuf(stdout, NULL, _IONBF, 0); signal(SIGPIPE, SIG_IGN); printf("[*] Starting reproducer for WARNING in svc_destroy...\n"); int res = mkdir(MNT_DIR, 0755); if (res < 0 && errno != EEXIST) { printf("[-] Failed to mkdir %s: %s\n", MNT_DIR, strerror(errno)); exit(1); } printf("[+] Directory %s ready.\n", MNT_DIR); /* 1. Start background SunRPC server to respond to rpc_ping() during nfs4_alloc_client() */ int chosen_nfs_port = DEFAULT_NFS_PORT; int nfs_sfd = socket(AF_INET, SOCK_STREAM, 0); if (nfs_sfd < 0) { printf("[-] Failed to create AF_INET socket: %s\n", strerror(errno)); exit(1); } printf("[+] Created AF_INET socket for NFS dummy server.\n"); int opt = 1; res = setsockopt(nfs_sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); if (res < 0) { printf("[-] Failed to setsockopt SO_REUSEADDR: %s\n", strerror(errno)); exit(1); } printf("[+] setsockopt SO_REUSEADDR successful.\n"); struct sockaddr_in nfs_sin; memset(&nfs_sin, 0, sizeof(nfs_sin)); nfs_sin.sin_family = AF_INET; nfs_sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); nfs_sin.sin_port = htons(chosen_nfs_port); res = bind(nfs_sfd, (struct sockaddr *)&nfs_sin, sizeof(nfs_sin)); if (res < 0) { printf("[*] Failed to bind to port %d (%s), trying port 20499...\n", chosen_nfs_port, strerror(errno)); chosen_nfs_port = 20499; nfs_sin.sin_port = htons(chosen_nfs_port); res = bind(nfs_sfd, (struct sockaddr *)&nfs_sin, sizeof(nfs_sin)); if (res < 0) { printf("[-] Failed to bind nfs_sfd to port %d: %s\n", chosen_nfs_port, strerror(errno)); exit(1); } } printf("[+] Bound nfs_sfd to 127.0.0.1:%d successfully.\n", chosen_nfs_port); res = listen(nfs_sfd, 10); if (res < 0) { printf("[-] Failed to listen on nfs_sfd: %s\n", strerror(errno)); exit(1); } printf("[+] Listening on nfs_sfd port %d successfully.\n", chosen_nfs_port); pid_t server_pid = fork(); if (server_pid < 0) { printf("[-] Failed to fork NFS server process: %s\n", strerror(errno)); exit(1); } if (server_pid == 0) { prctl(PR_SET_PDEATHSIG, SIGKILL); run_nfs_server(nfs_sfd); } printf("[+] Started dummy NFS RPC server process (pid=%d).\n", server_pid); /* 2. Configure NFS callback TCP port via sysctl */ int sysctl_fd = open(SYSCTL_PATH, O_WRONLY); if (sysctl_fd < 0) { printf("[*] %s not found: %s. Attempting mount to autoload nfs module...\n", SYSCTL_PATH, strerror(errno)); mount("127.0.0.1:/", MNT_DIR, "nfs4", 0, "addr=127.0.0.1,clientaddr=127.0.0.1"); sysctl_fd = open(SYSCTL_PATH, O_WRONLY); if (sysctl_fd < 0) { printf("[-] Failed to open %s after mount attempt: %s\n", SYSCTL_PATH, strerror(errno)); exit(1); } } printf("[+] Successfully opened %s for writing.\n", SYSCTL_PATH); char port_buf[32]; int len = snprintf(port_buf, sizeof(port_buf), "%d\n", CB_PORT); res = write(sysctl_fd, port_buf, len); if (res < 0) { printf("[-] Failed to write port %d to %s: %s\n", CB_PORT, SYSCTL_PATH, strerror(errno)); close(sysctl_fd); exit(1); } printf("[+] Successfully wrote port %d to %s.\n", CB_PORT, SYSCTL_PATH); res = close(sysctl_fd); if (res < 0) { printf("[-] Failed to close %s: %s\n", SYSCTL_PATH, strerror(errno)); exit(1); } printf("[+] Successfully closed sysctl fd.\n"); /* 3. Bind an IPv6-only listener to [::]:CB_PORT (no SO_REUSEADDR or SO_REUSEPORT) */ int cb_sfd = socket(AF_INET6, SOCK_STREAM, 0); if (cb_sfd < 0) { printf("[-] Failed to create AF_INET6 socket: %s\n", strerror(errno)); exit(1); } printf("[+] AF_INET6 socket created successfully.\n"); int v6only = 1; res = setsockopt(cb_sfd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)); if (res < 0) { printf("[-] Failed to setsockopt IPV6_V6ONLY: %s\n", strerror(errno)); close(cb_sfd); exit(1); } printf("[+] setsockopt IPV6_V6ONLY successful.\n"); struct sockaddr_in6 cb_sin6; memset(&cb_sin6, 0, sizeof(cb_sin6)); cb_sin6.sin6_family = AF_INET6; cb_sin6.sin6_port = htons(CB_PORT); cb_sin6.sin6_addr = in6addr_any; res = bind(cb_sfd, (struct sockaddr *)&cb_sin6, sizeof(cb_sin6)); if (res < 0) { printf("[-] Failed to bind AF_INET6 socket to port %d: %s\n", CB_PORT, strerror(errno)); close(cb_sfd); exit(1); } printf("[+] Bound AF_INET6 socket to [::]:%d successfully.\n", CB_PORT); res = listen(cb_sfd, 1); if (res < 0) { printf("[-] Failed to listen on AF_INET6 socket: %s\n", strerror(errno)); close(cb_sfd); exit(1); } printf("[+] Listening on AF_INET6 socket port %d successfully.\n", CB_PORT); /* 4. Trigger NFSv4 mount: * - nfs4_alloc_client() succeeds because our dummy server answers rpc_ping() * - nfs4_init_client() calls nfs4_init_callback() -> nfs_callback_up() * - nfs4_callback_up_net() creates PF_INET callback listener on port 45454 (added to sv_permsocks) * - nfs4_callback_up_net() fails to bind PF_INET6 listener on port 45454 (-EADDRINUSE) * - svc_destroy() is called while sv_permsocks is not empty, triggering WARNING */ char opt_buf[256]; snprintf(opt_buf, sizeof(opt_buf), "addr=127.0.0.1,port=%d,clientaddr=127.0.0.1,sec=sys", chosen_nfs_port); printf("[*] Invoking mount with options: %s\n", opt_buf); res = mount("127.0.0.1:/", MNT_DIR, "nfs4", 0, opt_buf); if (res < 0) { printf("[+] mount returned expected error: %s (errno=%d)\n", strerror(errno), errno); } else { printf("[-] mount unexpectedly succeeded\n"); umount(MNT_DIR); } res = close(cb_sfd); if (res < 0) { printf("[-] Failed to close cb_sfd: %s\n", strerror(errno)); } else { printf("[+] Closed cb_sfd successfully.\n"); } if (server_pid > 0) { kill(server_pid, SIGKILL); waitpid(server_pid, NULL, 0); printf("[+] Stopped dummy NFS RPC server.\n"); } res = close(nfs_sfd); if (res < 0) { printf("[-] Failed to close nfs_sfd: %s\n", strerror(errno)); } else { printf("[+] Closed nfs_sfd successfully.\n"); } res = rmdir(MNT_DIR); if (res < 0) { printf("[-] Failed to rmdir %s: %s\n", MNT_DIR, strerror(errno)); } else { printf("[+] Removed directory %s successfully.\n", MNT_DIR); } sleep(1); printf("[+] Reproducer finished.\n"); return 0; }