Skip to content

Commit 4269139

Browse files
chuckleverJ. Bruce Fields
authored andcommitted
nfsd: Fix race between FREE_STATEID and LOCK
When running LTP's nfslock01 test, the Linux client can send a LOCK and a FREE_STATEID request at the same time. The outcome is: Frame 324 R OPEN stateid [2,O] Frame 115004 C LOCK lockowner_is_new stateid [2,O] offset 672000 len 64 Frame 115008 R LOCK stateid [1,L] Frame 115012 C WRITE stateid [0,L] offset 672000 len 64 Frame 115016 R WRITE NFS4_OK Frame 115019 C LOCKU stateid [1,L] offset 672000 len 64 Frame 115022 R LOCKU NFS4_OK Frame 115025 C FREE_STATEID stateid [2,L] Frame 115026 C LOCK lockowner_is_new stateid [2,O] offset 672128 len 64 Frame 115029 R FREE_STATEID NFS4_OK Frame 115030 R LOCK stateid [3,L] Frame 115034 C WRITE stateid [0,L] offset 672128 len 64 Frame 115038 R WRITE NFS4ERR_BAD_STATEID In other words, the server returns stateid L in a successful LOCK reply, but it has already released it. Subsequent uses of stateid L fail. To address this, protect the generation check in nfsd4_free_stateid with the st_mutex. This should guarantee that only one of two outcomes occurs: either LOCK returns a fresh valid stateid, or FREE_STATEID returns NFS4ERR_LOCKS_HELD. Reported-by: Alexey Kodanev <alexey.kodanev@oracle.com> Fix-suggested-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Tested-by: Alexey Kodanev <alexey.kodanev@oracle.com> Cc: stable@vger.kernel.org Signed-off-by: J. Bruce Fields <bfields@redhat.com>
1 parent 502aa0a commit 4269139

File tree

1 file changed

+28
-12
lines changed

1 file changed

+28
-12
lines changed

fs/nfsd/nfs4state.c

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4903,14 +4903,39 @@ nfsd4_test_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
49034903
return nfs_ok;
49044904
}
49054905

4906+
static __be32
4907+
nfsd4_free_lock_stateid(stateid_t *stateid, struct nfs4_stid *s)
4908+
{
4909+
struct nfs4_ol_stateid *stp = openlockstateid(s);
4910+
__be32 ret;
4911+
4912+
mutex_lock(&stp->st_mutex);
4913+
4914+
ret = check_stateid_generation(stateid, &s->sc_stateid, 1);
4915+
if (ret)
4916+
goto out;
4917+
4918+
ret = nfserr_locks_held;
4919+
if (check_for_locks(stp->st_stid.sc_file,
4920+
lockowner(stp->st_stateowner)))
4921+
goto out;
4922+
4923+
release_lock_stateid(stp);
4924+
ret = nfs_ok;
4925+
4926+
out:
4927+
mutex_unlock(&stp->st_mutex);
4928+
nfs4_put_stid(s);
4929+
return ret;
4930+
}
4931+
49064932
__be32
49074933
nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
49084934
struct nfsd4_free_stateid *free_stateid)
49094935
{
49104936
stateid_t *stateid = &free_stateid->fr_stateid;
49114937
struct nfs4_stid *s;
49124938
struct nfs4_delegation *dp;
4913-
struct nfs4_ol_stateid *stp;
49144939
struct nfs4_client *cl = cstate->session->se_client;
49154940
__be32 ret = nfserr_bad_stateid;
49164941

@@ -4929,18 +4954,9 @@ nfsd4_free_stateid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
49294954
ret = nfserr_locks_held;
49304955
break;
49314956
case NFS4_LOCK_STID:
4932-
ret = check_stateid_generation(stateid, &s->sc_stateid, 1);
4933-
if (ret)
4934-
break;
4935-
stp = openlockstateid(s);
4936-
ret = nfserr_locks_held;
4937-
if (check_for_locks(stp->st_stid.sc_file,
4938-
lockowner(stp->st_stateowner)))
4939-
break;
4940-
WARN_ON(!unhash_lock_stateid(stp));
4957+
atomic_inc(&s->sc_count);
49414958
spin_unlock(&cl->cl_lock);
4942-
nfs4_put_stid(s);
4943-
ret = nfs_ok;
4959+
ret = nfsd4_free_lock_stateid(stateid, s);
49444960
goto out;
49454961
case NFS4_REVOKED_DELEG_STID:
49464962
dp = delegstateid(s);

0 commit comments

Comments
 (0)