aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <trond.myklebust@primarydata.com>2017-11-03 08:00:11 -0400
committerJ. Bruce Fields <bfields@redhat.com>2017-11-27 16:45:10 -0500
commitd8a1a000555ecd1b824ac1ed6df8fe364dfbbbb0 (patch)
treefe0ba252cdf5aed298444abfbdc989991ec0e84b
parent15ca08d3299682dc49bad73251677b2c5017ef08 (diff)
nfsd: Fix another OPEN stateid race
If nfsd4_process_open2() is initialising a new stateid, and yet the call to nfs4_get_vfs_file() fails for some reason, then we must declare the stateid closed, and unhash it before dropping the mutex. Right now, we unhash the stateid after dropping the mutex, and without changing the stateid type, meaning that another OPEN could theoretically look it up and attempt to use it. Reported-by: Andrew W Elble <aweits@rit.edu> Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com> Cc: stable@vger.kernel.org Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r--fs/nfsd/nfs4state.c28
1 files changed, 13 insertions, 15 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index ee8fde2dfa92..457f0e7ece74 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -4502,6 +4502,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
4502 struct nfs4_ol_stateid *stp = NULL; 4502 struct nfs4_ol_stateid *stp = NULL;
4503 struct nfs4_delegation *dp = NULL; 4503 struct nfs4_delegation *dp = NULL;
4504 __be32 status; 4504 __be32 status;
4505 bool new_stp = false;
4505 4506
4506 /* 4507 /*
4507 * Lookup file; if found, lookup stateid and check open request, 4508 * Lookup file; if found, lookup stateid and check open request,
@@ -4521,11 +4522,19 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
4521 goto out; 4522 goto out;
4522 } 4523 }
4523 4524
4525 if (!stp) {
4526 stp = init_open_stateid(fp, open);
4527 if (!open->op_stp)
4528 new_stp = true;
4529 }
4530
4524 /* 4531 /*
4525 * OPEN the file, or upgrade an existing OPEN. 4532 * OPEN the file, or upgrade an existing OPEN.
4526 * If truncate fails, the OPEN fails. 4533 * If truncate fails, the OPEN fails.
4534 *
4535 * stp is already locked.
4527 */ 4536 */
4528 if (stp) { 4537 if (!new_stp) {
4529 /* Stateid was found, this is an OPEN upgrade */ 4538 /* Stateid was found, this is an OPEN upgrade */
4530 status = nfs4_upgrade_open(rqstp, fp, current_fh, stp, open); 4539 status = nfs4_upgrade_open(rqstp, fp, current_fh, stp, open);
4531 if (status) { 4540 if (status) {
@@ -4533,22 +4542,11 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
4533 goto out; 4542 goto out;
4534 } 4543 }
4535 } else { 4544 } else {
4536 /* stp is returned locked. */
4537 stp = init_open_stateid(fp, open);
4538 /* See if we lost the race to some other thread */
4539 if (stp->st_access_bmap != 0) {
4540 status = nfs4_upgrade_open(rqstp, fp, current_fh,
4541 stp, open);
4542 if (status) {
4543 mutex_unlock(&stp->st_mutex);
4544 goto out;
4545 }
4546 goto upgrade_out;
4547 }
4548 status = nfs4_get_vfs_file(rqstp, fp, current_fh, stp, open); 4545 status = nfs4_get_vfs_file(rqstp, fp, current_fh, stp, open);
4549 if (status) { 4546 if (status) {
4550 mutex_unlock(&stp->st_mutex); 4547 stp->st_stid.sc_type = NFS4_CLOSED_STID;
4551 release_open_stateid(stp); 4548 release_open_stateid(stp);
4549 mutex_unlock(&stp->st_mutex);
4552 goto out; 4550 goto out;
4553 } 4551 }
4554 4552
@@ -4557,7 +4555,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
4557 if (stp->st_clnt_odstate == open->op_odstate) 4555 if (stp->st_clnt_odstate == open->op_odstate)
4558 open->op_odstate = NULL; 4556 open->op_odstate = NULL;
4559 } 4557 }
4560upgrade_out: 4558
4561 nfs4_inc_and_copy_stateid(&open->op_stateid, &stp->st_stid); 4559 nfs4_inc_and_copy_stateid(&open->op_stateid, &stp->st_stid);
4562 mutex_unlock(&stp->st_mutex); 4560 mutex_unlock(&stp->st_mutex);
4563 4561