Commit 15ca08d3 authored by Trond Myklebust's avatar Trond Myklebust Committed by J. Bruce Fields

nfsd: Fix stateid races between OPEN and CLOSE

Open file stateids can linger on the nfs4_file list of stateids even
after they have been closed. In order to avoid reusing such a
stateid, and confusing the client, we need to recheck the
nfs4_stid's type after taking the mutex.
Otherwise, we risk reusing an old stateid that was already closed,
which will confuse clients that expect new stateids to conform to
RFC7530 Sections 9.1.4.2 and 16.2.5 or RFC5661 Sections 8.2.2 and 18.2.4.
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
Cc: stable@vger.kernel.org
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent 4fbd8d19
...@@ -3562,7 +3562,9 @@ nfsd4_find_existing_open(struct nfs4_file *fp, struct nfsd4_open *open) ...@@ -3562,7 +3562,9 @@ nfsd4_find_existing_open(struct nfs4_file *fp, struct nfsd4_open *open)
/* ignore lock owners */ /* ignore lock owners */
if (local->st_stateowner->so_is_open_owner == 0) if (local->st_stateowner->so_is_open_owner == 0)
continue; continue;
if (local->st_stateowner == &oo->oo_owner) { if (local->st_stateowner != &oo->oo_owner)
continue;
if (local->st_stid.sc_type == NFS4_OPEN_STID) {
ret = local; ret = local;
refcount_inc(&ret->st_stid.sc_count); refcount_inc(&ret->st_stid.sc_count);
break; break;
...@@ -3571,6 +3573,52 @@ nfsd4_find_existing_open(struct nfs4_file *fp, struct nfsd4_open *open) ...@@ -3571,6 +3573,52 @@ nfsd4_find_existing_open(struct nfs4_file *fp, struct nfsd4_open *open)
return ret; return ret;
} }
static __be32
nfsd4_verify_open_stid(struct nfs4_stid *s)
{
__be32 ret = nfs_ok;
switch (s->sc_type) {
default:
break;
case NFS4_CLOSED_STID:
case NFS4_CLOSED_DELEG_STID:
ret = nfserr_bad_stateid;
break;
case NFS4_REVOKED_DELEG_STID:
ret = nfserr_deleg_revoked;
}
return ret;
}
/* Lock the stateid st_mutex, and deal with races with CLOSE */
static __be32
nfsd4_lock_ol_stateid(struct nfs4_ol_stateid *stp)
{
__be32 ret;
mutex_lock(&stp->st_mutex);
ret = nfsd4_verify_open_stid(&stp->st_stid);
if (ret != nfs_ok)
mutex_unlock(&stp->st_mutex);
return ret;
}
static struct nfs4_ol_stateid *
nfsd4_find_and_lock_existing_open(struct nfs4_file *fp, struct nfsd4_open *open)
{
struct nfs4_ol_stateid *stp;
for (;;) {
spin_lock(&fp->fi_lock);
stp = nfsd4_find_existing_open(fp, open);
spin_unlock(&fp->fi_lock);
if (!stp || nfsd4_lock_ol_stateid(stp) == nfs_ok)
break;
nfs4_put_stid(&stp->st_stid);
}
return stp;
}
static struct nfs4_openowner * static struct nfs4_openowner *
alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open, alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open,
struct nfsd4_compound_state *cstate) struct nfsd4_compound_state *cstate)
...@@ -3615,6 +3663,7 @@ init_open_stateid(struct nfs4_file *fp, struct nfsd4_open *open) ...@@ -3615,6 +3663,7 @@ init_open_stateid(struct nfs4_file *fp, struct nfsd4_open *open)
mutex_init(&stp->st_mutex); mutex_init(&stp->st_mutex);
mutex_lock(&stp->st_mutex); mutex_lock(&stp->st_mutex);
retry:
spin_lock(&oo->oo_owner.so_client->cl_lock); spin_lock(&oo->oo_owner.so_client->cl_lock);
spin_lock(&fp->fi_lock); spin_lock(&fp->fi_lock);
...@@ -3639,7 +3688,11 @@ init_open_stateid(struct nfs4_file *fp, struct nfsd4_open *open) ...@@ -3639,7 +3688,11 @@ init_open_stateid(struct nfs4_file *fp, struct nfsd4_open *open)
spin_unlock(&fp->fi_lock); spin_unlock(&fp->fi_lock);
spin_unlock(&oo->oo_owner.so_client->cl_lock); spin_unlock(&oo->oo_owner.so_client->cl_lock);
if (retstp) { if (retstp) {
mutex_lock(&retstp->st_mutex); /* Handle races with CLOSE */
if (nfsd4_lock_ol_stateid(retstp) != nfs_ok) {
nfs4_put_stid(&retstp->st_stid);
goto retry;
}
/* To keep mutex tracking happy */ /* To keep mutex tracking happy */
mutex_unlock(&stp->st_mutex); mutex_unlock(&stp->st_mutex);
stp = retstp; stp = retstp;
...@@ -4460,9 +4513,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf ...@@ -4460,9 +4513,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
status = nfs4_check_deleg(cl, open, &dp); status = nfs4_check_deleg(cl, open, &dp);
if (status) if (status)
goto out; goto out;
spin_lock(&fp->fi_lock); stp = nfsd4_find_and_lock_existing_open(fp, open);
stp = nfsd4_find_existing_open(fp, open);
spin_unlock(&fp->fi_lock);
} else { } else {
open->op_file = NULL; open->op_file = NULL;
status = nfserr_bad_stateid; status = nfserr_bad_stateid;
...@@ -4476,7 +4527,6 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf ...@@ -4476,7 +4527,6 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
*/ */
if (stp) { if (stp) {
/* Stateid was found, this is an OPEN upgrade */ /* Stateid was found, this is an OPEN upgrade */
mutex_lock(&stp->st_mutex);
status = nfs4_upgrade_open(rqstp, fp, current_fh, stp, open); status = nfs4_upgrade_open(rqstp, fp, current_fh, stp, open);
if (status) { if (status) {
mutex_unlock(&stp->st_mutex); mutex_unlock(&stp->st_mutex);
...@@ -5367,7 +5417,6 @@ static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s) ...@@ -5367,7 +5417,6 @@ static void nfsd4_close_open_stateid(struct nfs4_ol_stateid *s)
bool unhashed; bool unhashed;
LIST_HEAD(reaplist); LIST_HEAD(reaplist);
s->st_stid.sc_type = NFS4_CLOSED_STID;
spin_lock(&clp->cl_lock); spin_lock(&clp->cl_lock);
unhashed = unhash_open_stateid(s, &reaplist); unhashed = unhash_open_stateid(s, &reaplist);
...@@ -5407,10 +5456,12 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, ...@@ -5407,10 +5456,12 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
nfsd4_bump_seqid(cstate, status); nfsd4_bump_seqid(cstate, status);
if (status) if (status)
goto out; goto out;
stp->st_stid.sc_type = NFS4_CLOSED_STID;
nfs4_inc_and_copy_stateid(&close->cl_stateid, &stp->st_stid); nfs4_inc_and_copy_stateid(&close->cl_stateid, &stp->st_stid);
mutex_unlock(&stp->st_mutex);
nfsd4_close_open_stateid(stp); nfsd4_close_open_stateid(stp);
mutex_unlock(&stp->st_mutex);
/* put reference from nfs4_preprocess_seqid_op */ /* put reference from nfs4_preprocess_seqid_op */
nfs4_put_stid(&stp->st_stid); nfs4_put_stid(&stp->st_stid);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment