Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3dbacee6 authored by Trond Myklebust's avatar Trond Myklebust Committed by J. Bruce Fields
Browse files

nfsd: Protect unconfirmed client creation using client_lock



...instead of relying on the client_mutex.

Signed-off-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent 5cc40fd7
Loading
Loading
Loading
Loading
+22 −11
Original line number Original line Diff line number Diff line
@@ -1923,7 +1923,7 @@ add_to_unconfirmed(struct nfs4_client *clp)
	add_clp_to_name_tree(clp, &nn->unconf_name_tree);
	add_clp_to_name_tree(clp, &nn->unconf_name_tree);
	idhashval = clientid_hashval(clp->cl_clientid.cl_id);
	idhashval = clientid_hashval(clp->cl_clientid.cl_id);
	list_add(&clp->cl_idhash, &nn->unconf_id_hashtbl[idhashval]);
	list_add(&clp->cl_idhash, &nn->unconf_id_hashtbl[idhashval]);
	renew_client(clp);
	renew_client_locked(clp);
}
}


static void
static void
@@ -1937,7 +1937,7 @@ move_to_confirmed(struct nfs4_client *clp)
	rb_erase(&clp->cl_namenode, &nn->unconf_name_tree);
	rb_erase(&clp->cl_namenode, &nn->unconf_name_tree);
	add_clp_to_name_tree(clp, &nn->conf_name_tree);
	add_clp_to_name_tree(clp, &nn->conf_name_tree);
	set_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags);
	set_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags);
	renew_client(clp);
	renew_client_locked(clp);
}
}


static struct nfs4_client *
static struct nfs4_client *
@@ -1950,7 +1950,7 @@ find_client_in_id_table(struct list_head *tbl, clientid_t *clid, bool sessions)
		if (same_clid(&clp->cl_clientid, clid)) {
		if (same_clid(&clp->cl_clientid, clid)) {
			if ((bool)clp->cl_minorversion != sessions)
			if ((bool)clp->cl_minorversion != sessions)
				return NULL;
				return NULL;
			renew_client(clp);
			renew_client_locked(clp);
			return clp;
			return clp;
		}
		}
	}
	}
@@ -2152,7 +2152,8 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
		  struct nfsd4_compound_state *cstate,
		  struct nfsd4_compound_state *cstate,
		  struct nfsd4_exchange_id *exid)
		  struct nfsd4_exchange_id *exid)
{
{
	struct nfs4_client *unconf, *conf, *new;
	struct nfs4_client *conf, *new;
	struct nfs4_client *unconf = NULL;
	__be32 status;
	__be32 status;
	char			addr_str[INET6_ADDRSTRLEN];
	char			addr_str[INET6_ADDRSTRLEN];
	nfs4_verifier		verf = exid->verifier;
	nfs4_verifier		verf = exid->verifier;
@@ -2187,6 +2188,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,


	/* Cases below refer to rfc 5661 section 18.35.4: */
	/* Cases below refer to rfc 5661 section 18.35.4: */
	nfs4_lock_state();
	nfs4_lock_state();
	spin_lock(&nn->client_lock);
	conf = find_confirmed_client_by_name(&exid->clname, nn);
	conf = find_confirmed_client_by_name(&exid->clname, nn);
	if (conf) {
	if (conf) {
		bool creds_match = same_creds(&conf->cl_cred, &rqstp->rq_cred);
		bool creds_match = same_creds(&conf->cl_cred, &rqstp->rq_cred);
@@ -2218,7 +2220,6 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
				status = nfserr_clid_inuse;
				status = nfserr_clid_inuse;
				goto out;
				goto out;
			}
			}
			expire_client(conf);
			goto out_new;
			goto out_new;
		}
		}
		if (verfs_match) { /* case 2 */
		if (verfs_match) { /* case 2 */
@@ -2226,6 +2227,7 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
			goto out_copy;
			goto out_copy;
		}
		}
		/* case 5, client reboot */
		/* case 5, client reboot */
		conf = NULL;
		goto out_new;
		goto out_new;
	}
	}


@@ -2236,17 +2238,18 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,


	unconf  = find_unconfirmed_client_by_name(&exid->clname, nn);
	unconf  = find_unconfirmed_client_by_name(&exid->clname, nn);
	if (unconf) /* case 4, possible retry or client restart */
	if (unconf) /* case 4, possible retry or client restart */
		expire_client(unconf);
		unhash_client_locked(unconf);


	/* case 1 (normal case) */
	/* case 1 (normal case) */
out_new:
out_new:
	if (conf)
		unhash_client_locked(conf);
	new->cl_minorversion = cstate->minorversion;
	new->cl_minorversion = cstate->minorversion;
	new->cl_mach_cred = (exid->spa_how == SP4_MACH_CRED);
	new->cl_mach_cred = (exid->spa_how == SP4_MACH_CRED);


	gen_clid(new, nn);
	gen_clid(new, nn);
	add_to_unconfirmed(new);
	add_to_unconfirmed(new);
	conf = new;
	swap(new, conf);
	new = NULL;
out_copy:
out_copy:
	exid->clientid.cl_boot = conf->cl_clientid.cl_boot;
	exid->clientid.cl_boot = conf->cl_clientid.cl_boot;
	exid->clientid.cl_id = conf->cl_clientid.cl_id;
	exid->clientid.cl_id = conf->cl_clientid.cl_id;
@@ -2259,9 +2262,12 @@ nfsd4_exchange_id(struct svc_rqst *rqstp,
	status = nfs_ok;
	status = nfs_ok;


out:
out:
	spin_unlock(&nn->client_lock);
	nfs4_unlock_state();
	nfs4_unlock_state();
	if (new)
	if (new)
		free_client(new);
		expire_client(new);
	if (unconf)
		expire_client(unconf);
	return status;
	return status;
}
}


@@ -2900,7 +2906,8 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
{
{
	struct xdr_netobj 	clname = setclid->se_name;
	struct xdr_netobj 	clname = setclid->se_name;
	nfs4_verifier		clverifier = setclid->se_verf;
	nfs4_verifier		clverifier = setclid->se_verf;
	struct nfs4_client	*conf, *unconf, *new;
	struct nfs4_client	*conf, *new;
	struct nfs4_client	*unconf = NULL;
	__be32 			status;
	__be32 			status;
	struct nfsd_net		*nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
	struct nfsd_net		*nn = net_generic(SVC_NET(rqstp), nfsd_net_id);


@@ -2909,6 +2916,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
		return nfserr_jukebox;
		return nfserr_jukebox;
	/* Cases below refer to rfc 3530 section 14.2.33: */
	/* Cases below refer to rfc 3530 section 14.2.33: */
	nfs4_lock_state();
	nfs4_lock_state();
	spin_lock(&nn->client_lock);
	conf = find_confirmed_client_by_name(&clname, nn);
	conf = find_confirmed_client_by_name(&clname, nn);
	if (conf) {
	if (conf) {
		/* case 0: */
		/* case 0: */
@@ -2926,7 +2934,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	}
	}
	unconf = find_unconfirmed_client_by_name(&clname, nn);
	unconf = find_unconfirmed_client_by_name(&clname, nn);
	if (unconf)
	if (unconf)
		expire_client(unconf);
		unhash_client_locked(unconf);
	if (conf && same_verf(&conf->cl_verifier, &clverifier))
	if (conf && same_verf(&conf->cl_verifier, &clverifier))
		/* case 1: probable callback update */
		/* case 1: probable callback update */
		copy_clid(new, conf);
		copy_clid(new, conf);
@@ -2941,9 +2949,12 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	new = NULL;
	new = NULL;
	status = nfs_ok;
	status = nfs_ok;
out:
out:
	spin_unlock(&nn->client_lock);
	nfs4_unlock_state();
	nfs4_unlock_state();
	if (new)
	if (new)
		free_client(new);
		free_client(new);
	if (unconf)
		expire_client(unconf);
	return status;
	return status;
}
}