Skip to contentSkip to conversation

nfsd4: fix unlikely race in session replay case

PATCH In review J. Bruce Fields 2 participants 4 messages Apr 28, 2010 – May 3, 2010

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 nfsd4: fix unlikely race in session replay case

From: J. Bruce Fields <[email protected]>

In the replay case, the

	renew_client(session->se_client);

happens after we've droppped the sessionid_lock, and without holding a reference on the session; so there's nothing preventing the session being freed before we get here.

---
 fs/nfsd/nfs4proc.c  |    3 +++
 fs/nfsd/nfs4state.c |    7 +++----
 fs/nfsd/nfs4xdr.c   |    1 -
 3 files changed, 6 insertions(+), 5 deletions(-)
fs/nfsd/nfs4proc.c +3 -0
index e147dbc..e4a4a3e 100644
@@ -1027,6 +1027,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
 	resp->rqstp = rqstp;
 	resp->cstate.minorversion = args->minorversion;
 	resp->cstate.replay_owner = NULL;
+	resp->cstate.session = NULL;
 	fh_init(&resp->cstate.current_fh, NFS4_FHSIZE);
 	fh_init(&resp->cstate.save_fh, NFS4_FHSIZE);
 	/* Use the deferral mechanism only for NFSv4.0 compounds */
@@ -1131,6 +1132,8 @@ encode_op:
 	fh_put(&resp->cstate.save_fh);
 	BUG_ON(resp->cstate.replay_owner);
 out:
+	if (resp->cstate.session)
+		nfsd4_put_session(resp->cstate.session);
 	nfsd4_release_compoundargs(args);
 	/* Reset deferral mechanism for RPC deferrals */
 	rqstp->rq_usedeferral = 1;
fs/nfsd/nfs4state.c +3 -4
index bba9fff..737315c 100644
@@ -1443,11 +1443,10 @@ nfsd4_sequence(struct svc_rqst *rqstp,
 	cstate->slot = slot;
 	cstate->session = session;
 
-	/* Hold a session reference until done processing the compound:
-	 * nfsd4_put_session called only if the cstate slot is set.
-	 */
-	nfsd4_get_session(session);
 out:
+	/* Hold a session reference until done processing the compound. */
+	if (cstate->session)
+		nfsd4_get_session(cstate->session);
 	spin_unlock(&sessionid_lock);
 	/* Renew the clientid on success and on replay */
 	if (cstate->session) {
fs/nfsd/nfs4xdr.c +0 -1
index fb27b1d..903c5a6 100644
@@ -3310,7 +3310,6 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
 		nfsd4_store_cache_entry(resp);
 		dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__);
 		resp->cstate.slot->sl_inuse = false;
-		nfsd4_put_session(resp->cstate.session);
 	}
 	return 1;
 }
Signature · 1 line
1.6.3.3
#1 lore raw
Quoted 30 lines · J. Bruce Fields

From: J. Bruce Fields <[email protected]>

In the replay case, the

	renew_client(session->se_client);

happens after we've droppped the sessionid_lock, and without holding a reference on the session; so there's nothing preventing the session being freed before we get here.

---
 fs/nfsd/nfs4proc.c  |    3 +++
 fs/nfsd/nfs4state.c |    7 +++----
 fs/nfsd/nfs4xdr.c   |    1 -
 3 files changed, 6 insertions(+), 5 deletions(-)
fs/nfsd/nfs4proc.c +1 -0
index e147dbc..e4a4a3e 100644
@@ -1027,6 +1027,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
 	resp->rqstp = rqstp;
 	resp->cstate.minorversion = args->minorversion;
 	resp->cstate.replay_owner = NULL;
+	resp->cstate.session = NULL;
 	fh_init(&resp->cstate.current_fh, NFS4_FHSIZE);
 	fh_init(&resp->cstate.save_fh, NFS4_FHSIZE);
 	/* Use the deferral mechanism only for NFSv4.0 compounds */

fs/nfsd/nfs4proc.c encode_op: · line 1136

Quoted 5 lines
fs/nfsd/nfs4proc.c +2 -0
@@ -1131,6 +1132,8 @@ encode_op:
 	fh_put(&resp->cstate.save_fh);
 	BUG_ON(resp->cstate.replay_owner);
 out:
+	if (resp->cstate.session)
+		nfsd4_put_session(resp->cstate.session);

Bruce, I see what you wanted to do but unfortunately putting the session here won't work since resp->cstate.slot is referred to later on in nfs4svc_encode_compoundres and it points into the slot table which is a member of the same session, therefore the current place in which it gets dereferenced should be maintained.

Please see below for another suggestion

Quoted 27 lines
fs/nfsd/nfs4proc.c +0 -0
 	nfsd4_release_compoundargs(args);
 	/* Reset deferral mechanism for RPC deferrals */
 	rqstp->rq_usedeferral = 1;
fs/nfsd/nfs4state.c +3 -4
index bba9fff..737315c 100644
@@ -1443,11 +1443,10 @@ nfsd4_sequence(struct svc_rqst *rqstp,
 	cstate->slot = slot;
 	cstate->session = session;
 
-	/* Hold a session reference until done processing the compound:
-	 * nfsd4_put_session called only if the cstate slot is set.
-	 */
-	nfsd4_get_session(session);
 out:
+	/* Hold a session reference until done processing the compound. */
+	if (cstate->session)
+		nfsd4_get_session(cstate->session);
 	spin_unlock(&sessionid_lock);
 	/* Renew the clientid on success and on replay */
 	if (cstate->session) {
fs/nfsd/nfs4xdr.c +0 -0
index fb27b1d..903c5a6 100644

fs/nfsd/nfs4xdr.c nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo · line 3315

Quoted 7 lines
fs/nfsd/nfs4xdr.c +0 -1
@@ -3310,7 +3310,6 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
 		nfsd4_store_cache_entry(resp);
 		dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__);
 		resp->cstate.slot->sl_inuse = false;
-		nfsd4_put_session(resp->cstate.session);
 	}
 	return 1;
 }

How about this instead:

fs/nfsd/nfs4xdr.c +6 -4
index 34ccf81..b04583c 100644
@@ -3307,10 +3307,12 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
 		iov = &rqstp->rq_res.head[0];
 	iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base;
 	BUG_ON(iov->iov_len > PAGE_SIZE);
-	if (nfsd4_has_session(cs) && cs->status != nfserr_replay_cache) {
-		nfsd4_store_cache_entry(resp);
-		dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__);
-		resp->cstate.slot->sl_inuse = false;
+	if (nfsd4_has_session(cs)) {
+		if (cs->status != nfserr_replay_cache) {
+			nfsd4_store_cache_entry(resp);
+			dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__);
+			resp->cstate.slot->sl_inuse = false;
+		}
 		nfsd4_put_session(resp->cstate.session);
 	}
 	return 1;
#2 lore raw
Quoted 13 lines · Benny Halevy

Review encode_op: · line 1136

Quoted 6 lines · J. Bruce Fields
(quoted diff) +2 -0
@@ -1131,6 +1132,8 @@ encode_op:
 	fh_put(&resp->cstate.save_fh);
 	BUG_ON(resp->cstate.replay_owner);
 out:
+	if (resp->cstate.session)
+		nfsd4_put_session(resp->cstate.session);

Bruce, I see what you wanted to do but unfortunately putting the session here won't work since resp->cstate.slot is referred to later on in nfs4svc_encode_compoundres and it points into the slot table which is a member of the same session, therefore the current place in which it gets dereferenced should be maintained.

Whoops, good catch! (Since most of the xdr encoding is done before nfsd4_proc_compound() exits, I think I must have forgotten that nfs4svc_encode_compoundres() is an exception?)

Quoted 1 line

Please see below for another suggestion

Done as you suggest. I'd applied the patch locally but not pushed it out yet, so I've updated it in place; the result is the following. I'll apply and push that to for-2.6.35 if you don't see any further probleme.

--b.

commit 44643622678d011642531915442d404ad14eb6c6

Author: J. Bruce Fields <[email protected]>
Date:   Sat Apr 24 15:35:43 2010 -0400

nfsd4: fix unlikely race in session replay case

In the replay case, the

renew_client(session->se_client);

happens after we've droppped the sessionid_lock, and without holding a reference on the session; so there's nothing preventing the session being freed before we get here.

Thanks to Benny Halevy for catching a bug in an earlier version of this patch.

fs/nfsd/nfs4proc.c +1 -0
index e147dbc..61282f8 100644
@@ -1027,6 +1027,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
 	resp->rqstp = rqstp;
 	resp->cstate.minorversion = args->minorversion;
 	resp->cstate.replay_owner = NULL;
+	resp->cstate.session = NULL;
 	fh_init(&resp->cstate.current_fh, NFS4_FHSIZE);
 	fh_init(&resp->cstate.save_fh, NFS4_FHSIZE);
 	/* Use the deferral mechanism only for NFSv4.0 compounds */
fs/nfsd/nfs4state.c +3 -4
index bba9fff..737315c 100644
@@ -1443,11 +1443,10 @@ nfsd4_sequence(struct svc_rqst *rqstp,
 	cstate->slot = slot;
 	cstate->session = session;
 
-	/* Hold a session reference until done processing the compound:
-	 * nfsd4_put_session called only if the cstate slot is set.
-	 */
-	nfsd4_get_session(session);
 out:
+	/* Hold a session reference until done processing the compound. */
+	if (cstate->session)
+		nfsd4_get_session(cstate->session);
 	spin_unlock(&sessionid_lock);
 	/* Renew the clientid on success and on replay */
 	if (cstate->session) {
fs/nfsd/nfs4xdr.c +6 -4
index fb27b1d..05bc5bd 100644
@@ -3306,10 +3306,12 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
 		iov = &rqstp->rq_res.head[0];
 	iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base;
 	BUG_ON(iov->iov_len > PAGE_SIZE);
-	if (nfsd4_has_session(cs) && cs->status != nfserr_replay_cache) {
-		nfsd4_store_cache_entry(resp);
-		dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__);
-		resp->cstate.slot->sl_inuse = false;
+	if (nfsd4_has_session(cs)) {
+		if (cs->status != nfserr_replay_cache) {
+			nfsd4_store_cache_entry(resp);
+			dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__);
+			resp->cstate.slot->sl_inuse = false;
+		}
 		nfsd4_put_session(resp->cstate.session);
 	}
 	return 1;
Acked-by
#3 lore raw
Quoted 36 lines · J. Bruce Fields
Quoted 12 lines · Benny Halevy

Review encode_op: · line 1136

Quoted 6 lines · J. Bruce Fields
(quoted diff) +2 -0
@@ -1131,6 +1132,8 @@ encode_op:
 	fh_put(&resp->cstate.save_fh);
 	BUG_ON(resp->cstate.replay_owner);
 out:
+	if (resp->cstate.session)
+		nfsd4_put_session(resp->cstate.session);

Bruce, I see what you wanted to do but unfortunately putting the session here won't work since resp->cstate.slot is referred to later on in nfs4svc_encode_compoundres and it points into the slot table which is a member of the same session, therefore the current place in which it gets dereferenced should be maintained.

Whoops, good catch! (Since most of the xdr encoding is done before nfsd4_proc_compound() exits, I think I must have forgotten that nfs4svc_encode_compoundres() is an exception?)

Quoted 1 line

Please see below for another suggestion

Done as you suggest. I'd applied the patch locally but not pushed it out yet, so I've updated it in place; the result is the following. I'll apply and push that to for-2.6.35 if you don't see any further probleme.

--b.

commit 44643622678d011642531915442d404ad14eb6c6

Author: J. Bruce Fields <[email protected]>
Date:   Sat Apr 24 15:35:43 2010 -0400

nfsd4: fix unlikely race in session replay case

In the replay case, the

renew_client(session->se_client);

Quoted 8 lines

happens after we've droppped the sessionid_lock, and without holding a reference on the session; so there's nothing preventing the session being freed before we get here.

Thanks to Benny Halevy for catching a bug in an earlier version of this patch.

Quoted 57 lines
fs/nfsd/nfs4proc.c +1 -0
index e147dbc..61282f8 100644
@@ -1027,6 +1027,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
 	resp->rqstp = rqstp;
 	resp->cstate.minorversion = args->minorversion;
 	resp->cstate.replay_owner = NULL;
+	resp->cstate.session = NULL;
 	fh_init(&resp->cstate.current_fh, NFS4_FHSIZE);
 	fh_init(&resp->cstate.save_fh, NFS4_FHSIZE);
 	/* Use the deferral mechanism only for NFSv4.0 compounds */
fs/nfsd/nfs4state.c +3 -4
index bba9fff..737315c 100644
@@ -1443,11 +1443,10 @@ nfsd4_sequence(struct svc_rqst *rqstp,
 	cstate->slot = slot;
 	cstate->session = session;
 
-	/* Hold a session reference until done processing the compound:
-	 * nfsd4_put_session called only if the cstate slot is set.
-	 */
-	nfsd4_get_session(session);
 out:
+	/* Hold a session reference until done processing the compound. */
+	if (cstate->session)
+		nfsd4_get_session(cstate->session);
 	spin_unlock(&sessionid_lock);
 	/* Renew the clientid on success and on replay */
 	if (cstate->session) {
fs/nfsd/nfs4xdr.c +6 -5
index fb27b1d..05bc5bd 100644
@@ -3306,10 +3306,12 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
 		iov = &rqstp->rq_res.head[0];
 	iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base;
 	BUG_ON(iov->iov_len > PAGE_SIZE);
-	if (nfsd4_has_session(cs) && cs->status != nfserr_replay_cache) {
-		nfsd4_store_cache_entry(resp);
-		dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__);
-		resp->cstate.slot->sl_inuse = false;
+	if (nfsd4_has_session(cs)) {
+		if (cs->status != nfserr_replay_cache) {
+			nfsd4_store_cache_entry(resp);
+			dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__);
+			resp->cstate.slot->sl_inuse = false;
+		}
 		nfsd4_put_session(resp->cstate.session);
 	}
 	return 1;
--

To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html