git: c91861cc216d - stable/14 - nfscl: Do not use nfso_own for delayed nfsrpc_doclose()

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Wed, 01 May 2024 01:06:13 UTC
The branch stable/14 has been updated by rmacklem:

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

commit c91861cc216d5fdd4824eaace53d08c372f6cf73
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2024-04-26 03:58:21 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2024-05-01 01:04:52 +0000

    nfscl: Do not use nfso_own for delayed nfsrpc_doclose()
    
    When an initial attempt to close an NFSv4 lock returns NFSERR_DELAY,
    the open structure is put on a list for delayed closing.  When this
    is done, the nfso_own field is set to NULL, so it cannot be used by
    nfsrpc_doclose().
    
    Without this patch, the NFSv4 client can crash when a NFSv4 server
    replies NFSERR_DELAY to a Close operation.  Fortunately, most extant
    NFSv4 servers do not do this.  This patch avoids the crash for any
    that do return NFSERR_DELAY for Close.
    
    Found during a IETF bakeathon testing event this week.
    
    (cherry picked from commit 6251027c4252edb3b8f8fc359a40e610349e9af3)
---
 sys/fs/nfsclient/nfs_clrpcops.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c
index 75bb5236cb65..8c5532268287 100644
--- a/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/sys/fs/nfsclient/nfs_clrpcops.c
@@ -826,6 +826,7 @@ nfsrpc_doclose(struct nfsmount *nmp, struct nfsclopen *op, NFSPROC_T *p,
 	u_int64_t off = 0, len = 0;
 	u_int32_t type = NFSV4LOCKT_READ;
 	int error, do_unlock, trycnt;
+	bool own_not_null;
 
 	tcred = newnfs_getcred();
 	newnfs_copycred(&op->nfso_cred, tcred);
@@ -892,22 +893,29 @@ nfsrpc_doclose(struct nfsmount *nmp, struct nfsclopen *op, NFSPROC_T *p,
 	 * There could be other Opens for different files on the same
 	 * OpenOwner, so locking is required.
 	 */
-	NFSLOCKCLSTATE();
-	nfscl_lockexcl(&op->nfso_own->nfsow_rwlock, NFSCLSTATEMUTEXPTR);
-	NFSUNLOCKCLSTATE();
+	own_not_null = false;
+	if (op->nfso_own != NULL) {
+		own_not_null = true;
+		NFSLOCKCLSTATE();
+		nfscl_lockexcl(&op->nfso_own->nfsow_rwlock, NFSCLSTATEMUTEXPTR);
+		NFSUNLOCKCLSTATE();
+	}
 	do {
 		error = nfscl_tryclose(op, tcred, nmp, p, loop_on_delayed);
 		if (error == NFSERR_GRACE)
 			(void) nfs_catnap(PZERO, error, "nfs_close");
 	} while (error == NFSERR_GRACE);
-	NFSLOCKCLSTATE();
-	nfscl_lockunlock(&op->nfso_own->nfsow_rwlock);
+	if (own_not_null) {
+		NFSLOCKCLSTATE();
+		nfscl_lockunlock(&op->nfso_own->nfsow_rwlock);
+	}
 
 	LIST_FOREACH_SAFE(lp, &op->nfso_lock, nfsl_list, nlp)
 		nfscl_freelockowner(lp, 0);
 	if (freeop && error != NFSERR_DELAY)
 		nfscl_freeopen(op, 0, true);
-	NFSUNLOCKCLSTATE();
+	if (own_not_null)
+		NFSUNLOCKCLSTATE();
 	NFSFREECRED(tcred);
 	return (error);
 }