// https://syzkaller.appspot.com/bug?id=ab5f90dd59b81c649cb70558bcfa93add235ef09 #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include int main(void) { setvbuf(stdout, NULL, _IONBF, 0); printf("[*] Starting reproducer for KASAN slab-out-of-bounds in nfsd...\n"); const char *nfsd_path = "/proc/fs/nfsd"; char path[256]; /* Step 1: Ensure nfsd filesystem is mounted */ snprintf(path, sizeof(path), "%s/versions", nfsd_path); if (access(path, F_OK) != 0) { printf("[*] %s not accessible, attempting to mount nfsd on /proc/fs/nfsd...\n", path); int res = mkdir("/proc/fs/nfsd", 0755); if (res < 0 && errno != EEXIST) { printf("[-] Failed to mkdir /proc/fs/nfsd: %s\n", strerror(errno)); exit(1); } res = mount("nfsd", "/proc/fs/nfsd", "nfsd", 0, NULL); if (res < 0 && errno != EBUSY) { printf("[-] Failed to mount nfsd on /proc/fs/nfsd: %s\n", strerror(errno)); exit(1); } printf("[+] Mounted nfsd on /proc/fs/nfsd successfully.\n"); } else { printf("[+] nfsd is already mounted at %s.\n", nfsd_path); } /* Step 2: Check current thread count and reset if threads are running */ snprintf(path, sizeof(path), "%s/threads", nfsd_path); int tfd = open(path, O_RDONLY); if (tfd < 0) { printf("[-] Failed to open %s: %s\n", path, strerror(errno)); exit(1); } char tbuf[32]; memset(tbuf, 0, sizeof(tbuf)); ssize_t tr = read(tfd, tbuf, sizeof(tbuf) - 1); close(tfd); if (tr < 0) { printf("[-] Failed to read %s: %s\n", path, strerror(errno)); exit(1); } int cur_threads = atoi(tbuf); printf("[+] Current nfsd threads: %d\n", cur_threads); if (cur_threads > 0) { tfd = open(path, O_WRONLY); if (tfd >= 0) { write(tfd, "0\n", 2); close(tfd); printf("[+] Reset nfsd threads to 0.\n"); usleep(100000); } } /* Step 3: Configure NFS versions to disable v2/v3 and enable v4 */ snprintf(path, sizeof(path), "%s/versions", nfsd_path); int vfd = open(path, O_WRONLY); if (vfd < 0) { printf("[-] Failed to open %s: %s\n", path, strerror(errno)); exit(1); } const char *vers_str = "-2 -3 +4\n"; int res = write(vfd, vers_str, strlen(vers_str)); if (res < 0) { printf("[-] Failed to write '%s' to %s: %s\n", vers_str, path, strerror(errno)); close(vfd); exit(1); } printf("[+] Successfully configured NFS versions: %s", vers_str); close(vfd); /* Step 4: Create an unconnected TCP socket (TCP_CLOSE state) */ int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { printf("[-] Failed to create AF_INET SOCK_STREAM socket: %s\n", strerror(errno)); exit(1); } printf("[+] Created AF_INET SOCK_STREAM socket (fd %d).\n", sock); struct sockaddr_in sin; memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = htonl(INADDR_ANY); sin.sin_port = 0; res = bind(sock, (struct sockaddr *)&sin, sizeof(sin)); if (res < 0) { printf("[-] Failed to bind socket: %s\n", strerror(errno)); close(sock); exit(1); } printf("[+] Bound socket successfully (unconnected, not listening).\n"); /* Step 5: Register socket into /proc/fs/nfsd/portlist */ snprintf(path, sizeof(path), "%s/portlist", nfsd_path); int pfd = open(path, O_WRONLY); if (pfd < 0) { printf("[-] Failed to open %s: %s\n", path, strerror(errno)); close(sock); exit(1); } char port_buf[32]; snprintf(port_buf, sizeof(port_buf), "%d\n", sock); res = write(pfd, port_buf, strlen(port_buf)); if (res < 0) { printf("[-] Failed to write fd %d to %s: %s\n", sock, path, strerror(errno)); close(pfd); close(sock); exit(1); } printf("[+] Registered socket fd %d into %s successfully.\n", sock, path); close(pfd); /* Step 6: Start Thread 1 */ snprintf(path, sizeof(path), "%s/threads", nfsd_path); tfd = open(path, O_WRONLY); if (tfd < 0) { printf("[-] Failed to open %s: %s\n", path, strerror(errno)); close(sock); exit(1); } res = write(tfd, "1\n", 2); if (res < 0) { printf("[-] Failed to start thread 1 via %s: %s\n", path, strerror(errno)); close(tfd); close(sock); exit(1); } printf("[+] Started 1 nfsd thread successfully.\n"); close(tfd); /* Step 7: Wait for Thread 1 to evict the closed transport from sv_permsocks */ printf("[*] Waiting for Thread 1 to evict the closed transport from sv_permsocks...\n"); snprintf(path, sizeof(path), "%s/portlist", nfsd_path); for (int i = 0; i < 20; i++) { usleep(100000); /* 100ms */ int rfd = open(path, O_RDONLY); if (rfd >= 0) { char check_buf[128]; ssize_t n = read(rfd, check_buf, sizeof(check_buf) - 1); close(rfd); if (n == 0) { printf("[+] Confirmed: portlist is now empty (sv_permsocks empty) after %d ms.\n", (i + 1) * 100); break; } } } /* Step 8: Spawn Thread 2 to trigger out-of-bounds read at fs/nfsd/nfssvc.c:888 */ printf("[*] Spawning Thread 2 with empty sv_permsocks to trigger KASAN slab-out-of-bounds...\n"); snprintf(path, sizeof(path), "%s/threads", nfsd_path); tfd = open(path, O_WRONLY); if (tfd < 0) { printf("[-] Failed to open %s: %s\n", path, strerror(errno)); close(sock); exit(1); } res = write(tfd, "2\n", 2); if (res < 0) { printf("[-] Write 2 to %s returned error: %s\n", path, strerror(errno)); } else { printf("[+] Wrote 2 to %s successfully.\n", path); } close(tfd); close(sock); printf("[*] Sleeping to allow kernel thread to crash/report...\n"); sleep(2); printf("[+] Reproducer finished.\n"); return 0; }