git: dcfa3ee44da2 - main - nfsserver: Fix vrele() panic in nfsvno_open()

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Fri, 13 Jan 2023 00:50:03 UTC
The branch main has been updated by rmacklem:

URL: https://cgit.FreeBSD.org/src/commit/?id=dcfa3ee44da2b139f51a8aedb0f55735c6dfe3f3

commit dcfa3ee44da2b139f51a8aedb0f55735c6dfe3f3
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2023-01-13 00:45:26 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2023-01-13 00:48:53 +0000

    nfsserver: Fix vrele() panic in nfsvno_open()
    
    Commit 65127e982b94 removed a check for ni_startdir != NULL.
    This allowed the vrele(ndp->ni_dvp) to be called with
    a NULL argument.
    
    This patch adds a new boolean argument to nfsvno_open()
    that can be checked instead of ni_startdir, since mjg@ requested
    that ni_startdir not be used. (Discussed in PR#268828.)
    
    PR:     268828
    Reviewed by:    mjg
    Differential Revision:  https://reviews.freebsd.org/D38032
---
 sys/fs/nfs/nfs_var.h            |  2 +-
 sys/fs/nfsserver/nfs_nfsdport.c |  4 ++--
 sys/fs/nfsserver/nfs_nfsdserv.c | 13 ++++++++-----
 3 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h
index 6692bc19a725..f8fd2229095f 100644
--- a/sys/fs/nfs/nfs_var.h
+++ b/sys/fs/nfs/nfs_var.h
@@ -719,7 +719,7 @@ int nfsvno_statfs(vnode_t, struct statfs *);
 void nfsvno_getfs(struct nfsfsinfo *, int);
 void nfsvno_open(struct nfsrv_descript *, struct nameidata *, nfsquad_t,
     nfsv4stateid_t *, struct nfsstate *, int *, struct nfsvattr *, int32_t *,
-    int, NFSACL_T *, nfsattrbit_t *, struct ucred *,
+    int, NFSACL_T *, nfsattrbit_t *, struct ucred *, bool,
     struct nfsexstuff *, vnode_t *);
 int nfsvno_updfilerev(vnode_t, struct nfsvattr *, struct nfsrv_descript *,
     NFSPROC_T *);
diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c
index 665e2c00ce08..d02653823857 100644
--- a/sys/fs/nfsserver/nfs_nfsdport.c
+++ b/sys/fs/nfsserver/nfs_nfsdport.c
@@ -1835,7 +1835,7 @@ void
 nfsvno_open(struct nfsrv_descript *nd, struct nameidata *ndp,
     nfsquad_t clientid, nfsv4stateid_t *stateidp, struct nfsstate *stp,
     int *exclusive_flagp, struct nfsvattr *nvap, int32_t *cverf, int create,
-    NFSACL_T *aclp, nfsattrbit_t *attrbitp, struct ucred *cred,
+    NFSACL_T *aclp, nfsattrbit_t *attrbitp, struct ucred *cred, bool done_namei,
     struct nfsexstuff *exp, struct vnode **vpp)
 {
 	struct vnode *vp = NULL;
@@ -1918,7 +1918,7 @@ nfsvno_open(struct nfsrv_descript *nd, struct nameidata *ndp,
 		}
 	} else {
 		nfsvno_relpathbuf(ndp);
-		if (create == NFSV4OPEN_CREATE) {
+		if (done_namei && create == NFSV4OPEN_CREATE) {
 			if (ndp->ni_dvp == ndp->ni_vp)
 				vrele(ndp->ni_dvp);
 			else
diff --git a/sys/fs/nfsserver/nfs_nfsdserv.c b/sys/fs/nfsserver/nfs_nfsdserv.c
index 709dc84d5d91..0433e9cda656 100644
--- a/sys/fs/nfsserver/nfs_nfsdserv.c
+++ b/sys/fs/nfsserver/nfs_nfsdserv.c
@@ -2830,13 +2830,14 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
 	u_long *hashp;
 	NFSACL_T *aclp = NULL;
 	struct thread *p = curthread;
+	bool done_namei;
 
 #ifdef NFS4_ACL_EXTATTR_NAME
 	aclp = acl_alloc(M_WAITOK);
 	aclp->acl_cnt = 0;
 #endif
 	NFSZERO_ATTRBIT(&attrbits);
-	named.ni_startdir = NULL;
+	done_namei = false;
 	named.ni_cnd.cn_nameiop = 0;
 	NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
 	i = fxdr_unsigned(int, *(tl + 5));
@@ -3042,6 +3043,7 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
 		if (!nd->nd_repstat) {
 			nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp,
 			    &dirp);
+			done_namei = true;
 		} else {
 			vrele(dp);
 			nfsvno_relpathbuf(&named);
@@ -3049,7 +3051,7 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
 		if (create == NFSV4OPEN_CREATE) {
 		    switch (how) {
 		    case NFSCREATE_UNCHECKED:
-			if (named.ni_vp) {
+			if (done_namei && named.ni_vp != NULL) {
 				/*
 				 * Clear the setable attribute bits, except
 				 * for Size, if it is being truncated.
@@ -3061,12 +3063,13 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
 			}
 			break;
 		    case NFSCREATE_GUARDED:
-			if (named.ni_vp && !nd->nd_repstat)
+			if (done_namei && named.ni_vp != NULL &&
+			    nd->nd_repstat == 0)
 				nd->nd_repstat = EEXIST;
 			break;
 		    case NFSCREATE_EXCLUSIVE:
 			exclusive_flag = 1;
-			if (!named.ni_vp)
+			if (done_namei && named.ni_vp == NULL)
 				nva.na_mode = 0;
 			break;
 		    case NFSCREATE_EXCLUSIVE41:
@@ -3076,7 +3079,7 @@ nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
 		}
 		nfsvno_open(nd, &named, clientid, &stateid, stp,
 		    &exclusive_flag, &nva, cverf, create, aclp, &attrbits,
-		    nd->nd_cred, exp, &vp);
+		    nd->nd_cred, done_namei, exp, &vp);
 	} else if (claim == NFSV4OPEN_CLAIMPREVIOUS || claim ==
 	    NFSV4OPEN_CLAIMFH || claim == NFSV4OPEN_CLAIMDELEGATECURFH ||
 	    claim == NFSV4OPEN_CLAIMDELEGATEPREVFH) {