| From d8a1a000555ecd1b824ac1ed6df8fe364dfbbbb0 Mon Sep 17 00:00:00 2001 |
| From: Trond Myklebust <trond.myklebust@primarydata.com> |
| Date: Fri, 3 Nov 2017 08:00:11 -0400 |
| Subject: nfsd: Fix another OPEN stateid race |
| |
| From: Trond Myklebust <trond.myklebust@primarydata.com> |
| |
| commit d8a1a000555ecd1b824ac1ed6df8fe364dfbbbb0 upstream. |
| |
| 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> |
| Signed-off-by: J. Bruce Fields <bfields@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/nfsd/nfs4state.c | 28 +++++++++++++--------------- |
| 1 file changed, 13 insertions(+), 15 deletions(-) |
| |
| --- a/fs/nfsd/nfs4state.c |
| +++ b/fs/nfsd/nfs4state.c |
| @@ -4319,6 +4319,7 @@ nfsd4_process_open2(struct svc_rqst *rqs |
| struct nfs4_ol_stateid *stp = NULL; |
| struct nfs4_delegation *dp = NULL; |
| __be32 status; |
| + bool new_stp = false; |
| |
| /* |
| * Lookup file; if found, lookup stateid and check open request, |
| @@ -4338,11 +4339,19 @@ nfsd4_process_open2(struct svc_rqst *rqs |
| goto out; |
| } |
| |
| + if (!stp) { |
| + stp = init_open_stateid(fp, open); |
| + if (!open->op_stp) |
| + new_stp = true; |
| + } |
| + |
| /* |
| * OPEN the file, or upgrade an existing OPEN. |
| * If truncate fails, the OPEN fails. |
| + * |
| + * stp is already locked. |
| */ |
| - if (stp) { |
| + if (!new_stp) { |
| /* Stateid was found, this is an OPEN upgrade */ |
| status = nfs4_upgrade_open(rqstp, fp, current_fh, stp, open); |
| if (status) { |
| @@ -4350,22 +4359,11 @@ nfsd4_process_open2(struct svc_rqst *rqs |
| goto out; |
| } |
| } else { |
| - /* stp is returned locked. */ |
| - stp = init_open_stateid(fp, open); |
| - /* See if we lost the race to some other thread */ |
| - if (stp->st_access_bmap != 0) { |
| - status = nfs4_upgrade_open(rqstp, fp, current_fh, |
| - stp, open); |
| - if (status) { |
| - mutex_unlock(&stp->st_mutex); |
| - goto out; |
| - } |
| - goto upgrade_out; |
| - } |
| status = nfs4_get_vfs_file(rqstp, fp, current_fh, stp, open); |
| if (status) { |
| - mutex_unlock(&stp->st_mutex); |
| + stp->st_stid.sc_type = NFS4_CLOSED_STID; |
| release_open_stateid(stp); |
| + mutex_unlock(&stp->st_mutex); |
| goto out; |
| } |
| |
| @@ -4374,7 +4372,7 @@ nfsd4_process_open2(struct svc_rqst *rqs |
| if (stp->st_clnt_odstate == open->op_odstate) |
| open->op_odstate = NULL; |
| } |
| -upgrade_out: |
| + |
| nfs4_inc_and_copy_stateid(&open->op_stateid, &stp->st_stid); |
| mutex_unlock(&stp->st_mutex); |
| |