DragonFlyBSD Kernel Audit
DF-0767 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/vfs/nfs/nfs_vnops.c b/sys/vfs/nfs/nfs_vnops.c
--- a/sys/vfs/nfs/nfs_vnops.c
+++ b/sys/vfs/nfs/nfs_vnops.c
@@ -3085,8 +3085,24 @@
 		    np->n_fhsize = fhlen;
 		    newvp = NFSTOV(np);
 		} else if (NFS_CMPFH(dnp, nfhp, fhlen)) {
-		    vref(dvp);
-		    newvp = dvp;
+		    /*
+		     * The server echoed the parent directory's filehandle for this
+		     * lookup.  This branch is only reached when *npp == NULL (the
+		     * first 'if (*npp)' handles the update-existing case), i.e. the
+		     * create-style callers (nfs_create / nfs_mkdir / nfs_mknodrpc /
+		     * nfs_symlink) that asked nfs_lookitup() to allocate a NEW
+		     nfsnode.  Returning the directory vnode as the "new" object
+		     would corrupt those callers' vnode lock/ref accounting, and the
+		     local 'np' being left uninitialized here would (bug) cause the
+		     trailing '*npp = np' to store stack garbage which the caller
+		     dereferences via NFSTOV(np).  Treat the echoed handle as a
+		     collision so the create callers see EEXIST instead of panicking.
+		     A correct server never triggers this path (a post-create LOOKUP
+		     returns the new object's own filehandle, not the parent's).
+		     */
+		    m_freem(info.mrep);
+		    info.mrep = NULL;
+		    return (EEXIST);
 		} else {
 		    error = nfs_nget(dvp->v_mount, nfhp, fhlen, &np, NULL);
 		    if (error) {