aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
authorJeff Layton <jlayton@primarydata.com>2014-07-29 21:34:40 -0400
committerJ. Bruce Fields <bfields@redhat.com>2014-07-31 14:20:29 -0400
commitfc5a96c3b70d00c863f69ff4ea7f5dfddbcbc0d8 (patch)
tree8fe86bd3204e6476928e171530c777a32d6e58ba /fs/nfsd
parent3c1c995cc2e49f6f7504586ad07c5d80c6aa3301 (diff)
nfsd: close potential race in nfsd4_free_stateid
Once we remove the client_mutex, it'll be possible for the sc_type of a lock stateid to change after it's found and checked, but before we can go to destroy it. If that happens, we can end up putting the persistent reference to the stateid more than once, and unhash it more than once. Fix this by unhashing the lock stateid prior to dropping the cl_lock but after finding it. Signed-off-by: Jeff Layton <jlayton@primarydata.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd')
-rw-r--r--fs/nfsd/nfs4state.c21
1 files changed, 9 insertions, 12 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 9358cbe2283d..9c7dcbb68094 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4397,17 +4397,6 @@ unlock_state:
4397 return status; 4397 return status;
4398} 4398}
4399 4399
4400static __be32
4401nfsd4_free_lock_stateid(struct nfs4_ol_stateid *stp)
4402{
4403 struct nfs4_lockowner *lo = lockowner(stp->st_stateowner);
4404
4405 if (check_for_locks(stp->st_stid.sc_file, lo))
4406 return nfserr_locks_held;
4407 release_lock_stateid(stp);
4408 return nfs_ok;
4409}
4410
4411/* 4400/*
4412 * Test if the stateid is valid 4401 * Test if the stateid is valid
4413 */ 4402 */
@@ -4434,6 +4423,7 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
4434 stateid_t *stateid = &free_stateid->fr_stateid; 4423 stateid_t *stateid = &free_stateid->fr_stateid;
4435 struct nfs4_stid *s; 4424 struct nfs4_stid *s;
4436 struct nfs4_delegation *dp; 4425 struct nfs4_delegation *dp;
4426 struct nfs4_ol_stateid *stp;
4437 struct nfs4_client *cl = cstate->session->se_client; 4427 struct nfs4_client *cl = cstate->session->se_client;
4438 __be32 ret = nfserr_bad_stateid; 4428 __be32 ret = nfserr_bad_stateid;
4439 4429
@@ -4456,8 +4446,15 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
4456 ret = check_stateid_generation(stateid, &s->sc_stateid, 1); 4446 ret = check_stateid_generation(stateid, &s->sc_stateid, 1);
4457 if (ret) 4447 if (ret)
4458 break; 4448 break;
4449 stp = openlockstateid(s);
4450 ret = nfserr_locks_held;
4451 if (check_for_locks(stp->st_stid.sc_file,
4452 lockowner(stp->st_stateowner)))
4453 break;
4454 unhash_lock_stateid(stp);
4459 spin_unlock(&cl->cl_lock); 4455 spin_unlock(&cl->cl_lock);
4460 ret = nfsd4_free_lock_stateid(openlockstateid(s)); 4456 nfs4_put_stid(s);
4457 ret = nfs_ok;
4461 goto out; 4458 goto out;
4462 case NFS4_REVOKED_DELEG_STID: 4459 case NFS4_REVOKED_DELEG_STID:
4463 dp = delegstateid(s); 4460 dp = delegstateid(s);