diff options
author | Rick Macklem <rmacklem@FreeBSD.org> | 2024-04-26 03:58:21 +0000 |
---|---|---|
committer | Rick Macklem <rmacklem@FreeBSD.org> | 2024-04-26 03:58:21 +0000 |
commit | 6251027c4252edb3b8f8fc359a40e610349e9af3 (patch) | |
tree | 765ad5b6e16dd0a421e88932796a8729a3b10b69 | |
parent | f0e59ecff85d4b9e875464199e065a269c9c2530 (diff) | |
download | src-6251027c4252edb3b8f8fc359a40e610349e9af3.tar.gz src-6251027c4252edb3b8f8fc359a40e610349e9af3.zip |
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.
MFC after: 5 days
-rw-r--r-- | sys/fs/nfsclient/nfs_clrpcops.c | 20 |
1 files 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); } |