krpc: Acquire ref count of CLIENT for backchannel use
Michael Dexter <editor@callfortesting.org> reported a crash in FreeNAS, where the first argument to clnt_bck_svccall() was no longer valid. This argument is a pointer to the callback CLIENT structure, which is free'd when the associated NFSv4 ClientID is free'd. This appears to have occurred because a callback reply was still in the socket receive queue when the CLIENT structure was free'd. This patch acquires a reference count on the CLIENT that is not CLNT_RELEASE()'d until the socket structure is destroyed. This should guarantee that the CLIENT structure is still valid when clnt_bck_svccall() is called. It also adds a check for closed or closing to clnt_bck_svccall() so that it will not process the callback RPC reply message after the ClientID is free'd. Comments by: mav MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D30153
This commit is contained in:
parent
30b915d7b2
commit
e1a907a25c
@ -721,8 +721,8 @@ nfsrv_getclient(nfsquad_t clientid, int opflags, struct nfsclient **clpp,
|
||||
cbprogram, NFSV4_CBVERS);
|
||||
if (clp->lc_req.nr_client != NULL) {
|
||||
SVC_ACQUIRE(nd->nd_xprt);
|
||||
nd->nd_xprt->xp_p2 =
|
||||
clp->lc_req.nr_client->cl_private;
|
||||
CLNT_ACQUIRE(clp->lc_req.nr_client);
|
||||
nd->nd_xprt->xp_p2 = clp->lc_req.nr_client;
|
||||
/* Disable idle timeout. */
|
||||
nd->nd_xprt->xp_idletimeout = 0;
|
||||
nsep->sess_cbsess.nfsess_xprt = nd->nd_xprt;
|
||||
@ -6464,8 +6464,8 @@ nfsrv_bindconnsess(struct nfsrv_descript *nd, uint8_t *sessionid, int *foreaftp)
|
||||
"backchannel\n");
|
||||
savxprt = sep->sess_cbsess.nfsess_xprt;
|
||||
SVC_ACQUIRE(nd->nd_xprt);
|
||||
nd->nd_xprt->xp_p2 =
|
||||
clp->lc_req.nr_client->cl_private;
|
||||
CLNT_ACQUIRE(clp->lc_req.nr_client);
|
||||
nd->nd_xprt->xp_p2 = clp->lc_req.nr_client;
|
||||
/* Disable idle timeout. */
|
||||
nd->nd_xprt->xp_idletimeout = 0;
|
||||
sep->sess_cbsess.nfsess_xprt = nd->nd_xprt;
|
||||
|
@ -566,15 +566,26 @@ clnt_bck_destroy(CLIENT *cl)
|
||||
/*
|
||||
* This call is done by the svc code when a backchannel RPC reply is
|
||||
* received.
|
||||
* For the server end, where callback RPCs to the client are performed,
|
||||
* xp_p2 points to the "CLIENT" and not the associated "struct ct_data"
|
||||
* so that svc_vc_destroy() can CLNT_RELEASE() the reference count on it.
|
||||
*/
|
||||
void
|
||||
clnt_bck_svccall(void *arg, struct mbuf *mrep, uint32_t xid)
|
||||
{
|
||||
struct ct_data *ct = (struct ct_data *)arg;
|
||||
CLIENT *cl = (CLIENT *)arg;
|
||||
struct ct_data *ct;
|
||||
struct ct_request *cr;
|
||||
int foundreq;
|
||||
|
||||
ct = (struct ct_data *)cl->cl_private;
|
||||
mtx_lock(&ct->ct_lock);
|
||||
if (ct->ct_closing || ct->ct_closed) {
|
||||
mtx_unlock(&ct->ct_lock);
|
||||
m_freem(mrep);
|
||||
return;
|
||||
}
|
||||
|
||||
ct->ct_upcallrefs++;
|
||||
/*
|
||||
* See if we can match this reply to a request.
|
||||
|
@ -148,6 +148,11 @@ struct __rpc_svcthread;
|
||||
* reference count which tracks the number of currently assigned
|
||||
* worker threads plus one for the service pool's reference.
|
||||
* For NFSv4.1 sessions, a reference is also held for a backchannel.
|
||||
* xp_p2 - Points to the CLIENT structure for the RPC server end
|
||||
* (the client end for callbacks).
|
||||
* Points to the private structure (cl_private) for the
|
||||
* CLIENT structure for the RPC client end (the server
|
||||
* end for callbacks).
|
||||
*/
|
||||
typedef struct __rpc_svcxprt {
|
||||
#ifdef _KERNEL
|
||||
|
@ -500,6 +500,7 @@ static void
|
||||
svc_vc_destroy(SVCXPRT *xprt)
|
||||
{
|
||||
struct cf_conn *cd = (struct cf_conn *)xprt->xp_p1;
|
||||
CLIENT *cl = (CLIENT *)xprt->xp_p2;
|
||||
|
||||
SOCKBUF_LOCK(&xprt->xp_socket->so_rcv);
|
||||
if (xprt->xp_upcallset) {
|
||||
@ -509,6 +510,9 @@ svc_vc_destroy(SVCXPRT *xprt)
|
||||
}
|
||||
SOCKBUF_UNLOCK(&xprt->xp_socket->so_rcv);
|
||||
|
||||
if (cl != NULL)
|
||||
CLNT_RELEASE(cl);
|
||||
|
||||
svc_vc_destroy_common(xprt);
|
||||
|
||||
if (cd->mreq)
|
||||
|
Loading…
Reference in New Issue
Block a user