Skip to content

Commit e4469c6

Browse files
committed
NFSD: Fix the NFSv4.1 CREATE_SESSION operation
RFC 8881 Section 18.36.4 discusses the implementation of the NFSv4.1 CREATE_SESSION operation. The section defines four phases of operation. Phase 2 processes the CREATE_SESSION sequence ID. As a separate step, Phase 3 evaluates the CREATE_SESSION arguments. The problem we are concerned with is when phase 2 is successful but phase 3 fails. The spec language in this case is "No changes are made to any client records on the server." RFC 8881 Section 18.35.4 defines a "client record", and it does /not/ contain any details related to the special CREATE_SESSION slot. Therefore NFSD is incorrect to skip incrementing the CREATE_SESSION sequence id when phase 3 (see Section 18.36.4) of CREATE_SESSION processing fails. In other words, even though NFSD happens to store the cs_slot in a client record, in terms of the protocol the slot is logically separate from the client record. Three complications: 1. The world has moved on since commit 86c3e16 ("nfsd4: confirm only on succesful create_session") broke this. So we can't simply revert that commit. 2. NFSD's CREATE_SESSION implementation does not cleanly delineate the logic of phases 2 and 3. So this won't be a surgical fix. 3. Because of the way it currently handles the CREATE_SESSION slot sequence number, nfsd4_create_session() isn't caching error responses in the CREATE_SESSION slot. Instead of replaying the response cache in those cases, it's executing the transaction again. Reorganize the CREATE_SESSION slot sequence number accounting. This requires that error responses are appropriately cached in the CREATE_SESSION slot (once it is found). Reported-by: Connor Smith <[email protected]> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=218382 Signed-off-by: Chuck Lever <[email protected]>
1 parent f810402 commit e4469c6

File tree

1 file changed

+31
-26
lines changed

1 file changed

+31
-26
lines changed

fs/nfsd/nfs4state.c

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3562,6 +3562,9 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
35623562
new->cl_spo_must_allow.u.words[0] = exid->spo_must_allow[0];
35633563
new->cl_spo_must_allow.u.words[1] = exid->spo_must_allow[1];
35643564

3565+
/* Contrived initial CREATE_SESSION response */
3566+
new->cl_cs_slot.sl_status = nfserr_seq_misordered;
3567+
35653568
add_to_unconfirmed(new);
35663569
swap(new, conf);
35673570
out_copy:
@@ -3732,10 +3735,10 @@ nfsd4_create_session(struct svc_rqst *rqstp,
37323735
struct nfsd4_create_session *cr_ses = &u->create_session;
37333736
struct sockaddr *sa = svc_addr(rqstp);
37343737
struct nfs4_client *conf, *unconf;
3738+
struct nfsd4_clid_slot *cs_slot;
37353739
struct nfs4_client *old = NULL;
37363740
struct nfsd4_session *new;
37373741
struct nfsd4_conn *conn;
3738-
struct nfsd4_clid_slot *cs_slot = NULL;
37393742
__be32 status = 0;
37403743
struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
37413744

@@ -3761,50 +3764,51 @@ nfsd4_create_session(struct svc_rqst *rqstp,
37613764
spin_lock(&nn->client_lock);
37623765
unconf = find_unconfirmed_client(&cr_ses->clientid, true, nn);
37633766
conf = find_confirmed_client(&cr_ses->clientid, true, nn);
3764-
WARN_ON_ONCE(conf && unconf);
3767+
if (!conf && !unconf) {
3768+
status = nfserr_stale_clientid;
3769+
goto out_free_conn;
3770+
}
37653771

3766-
if (conf) {
3767-
status = nfserr_wrong_cred;
3768-
if (!nfsd4_mach_creds_match(conf, rqstp))
3769-
goto out_free_conn;
3772+
if (conf)
37703773
cs_slot = &conf->cl_cs_slot;
3771-
status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
3772-
if (status) {
3773-
if (status == nfserr_replay_cache)
3774-
status = nfsd4_replay_create_session(cr_ses, cs_slot);
3774+
else
3775+
cs_slot = &unconf->cl_cs_slot;
3776+
status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
3777+
if (status) {
3778+
if (status == nfserr_replay_cache) {
3779+
status = nfsd4_replay_create_session(cr_ses, cs_slot);
37753780
goto out_free_conn;
37763781
}
3777-
} else if (unconf) {
3782+
goto out_cache_error;
3783+
}
3784+
cs_slot->sl_seqid++;
3785+
cr_ses->seqid = cs_slot->sl_seqid;
3786+
3787+
if (conf) {
3788+
status = nfserr_wrong_cred;
3789+
if (!nfsd4_mach_creds_match(conf, rqstp))
3790+
goto out_cache_error;
3791+
} else {
37783792
status = nfserr_clid_inuse;
37793793
if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) ||
37803794
!rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) {
37813795
trace_nfsd_clid_cred_mismatch(unconf, rqstp);
3782-
goto out_free_conn;
3796+
goto out_cache_error;
37833797
}
37843798
status = nfserr_wrong_cred;
37853799
if (!nfsd4_mach_creds_match(unconf, rqstp))
3786-
goto out_free_conn;
3787-
cs_slot = &unconf->cl_cs_slot;
3788-
status = check_slot_seqid(cr_ses->seqid, cs_slot->sl_seqid, 0);
3789-
if (status) {
3790-
/* an unconfirmed replay returns misordered */
3791-
status = nfserr_seq_misordered;
3792-
goto out_free_conn;
3793-
}
3800+
goto out_cache_error;
37943801
old = find_confirmed_client_by_name(&unconf->cl_name, nn);
37953802
if (old) {
37963803
status = mark_client_expired_locked(old);
37973804
if (status) {
37983805
old = NULL;
3799-
goto out_free_conn;
3806+
goto out_cache_error;
38003807
}
38013808
trace_nfsd_clid_replaced(&old->cl_clientid);
38023809
}
38033810
move_to_confirmed(unconf);
38043811
conf = unconf;
3805-
} else {
3806-
status = nfserr_stale_clientid;
3807-
goto out_free_conn;
38083812
}
38093813
status = nfs_ok;
38103814
/* Persistent sessions are not supported */
@@ -3817,8 +3821,6 @@ nfsd4_create_session(struct svc_rqst *rqstp,
38173821

38183822
memcpy(cr_ses->sessionid.data, new->se_sessionid.data,
38193823
NFS4_MAX_SESSIONID_LEN);
3820-
cs_slot->sl_seqid++;
3821-
cr_ses->seqid = cs_slot->sl_seqid;
38223824

38233825
/* cache solo and embedded create sessions under the client_lock */
38243826
nfsd4_cache_create_session(cr_ses, cs_slot, status);
@@ -3831,6 +3833,9 @@ nfsd4_create_session(struct svc_rqst *rqstp,
38313833
if (old)
38323834
expire_client(old);
38333835
return status;
3836+
3837+
out_cache_error:
3838+
nfsd4_cache_create_session(cr_ses, cs_slot, status);
38343839
out_free_conn:
38353840
spin_unlock(&nn->client_lock);
38363841
free_conn(conn);

0 commit comments

Comments
 (0)