summaryrefslogtreecommitdiff
path: root/net/sunrpc
diff options
context:
space:
mode:
Diffstat (limited to 'net/sunrpc')
-rw-r--r--net/sunrpc/auth_gss/gss_rpc_xdr.c9
-rw-r--r--net/sunrpc/xprtrdma/ib_client.c24
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_recvfrom.c12
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_rw.c5
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_transport.c32
5 files changed, 55 insertions, 27 deletions
diff --git a/net/sunrpc/auth_gss/gss_rpc_xdr.c b/net/sunrpc/auth_gss/gss_rpc_xdr.c
index 3837e0a33d49..752c4e8ebd44 100644
--- a/net/sunrpc/auth_gss/gss_rpc_xdr.c
+++ b/net/sunrpc/auth_gss/gss_rpc_xdr.c
@@ -222,7 +222,8 @@ static int gssx_dec_linux_creds(struct xdr_stream *xdr,
return 0;
out_free_groups:
- groups_free(creds->cr_group_info);
+ put_group_info(creds->cr_group_info);
+ creds->cr_group_info = NULL;
return err;
}
@@ -243,12 +244,12 @@ static int gssx_dec_option_array(struct xdr_stream *xdr,
return 0;
/* we recognize only 1 currently: CREDS_VALUE */
- oa->count = 1;
-
oa->data = kmalloc(sizeof(struct gssx_option), GFP_KERNEL);
if (!oa->data)
return -ENOMEM;
+ oa->count = 1;
+
creds = kzalloc(sizeof(struct svc_cred), GFP_KERNEL);
if (!creds) {
err = -ENOMEM;
@@ -300,8 +301,10 @@ static int gssx_dec_option_array(struct xdr_stream *xdr,
return 0;
free_creds:
+ free_svc_cred(creds);
kfree(creds);
free_oa:
+ oa->count = 0;
kfree(oa->data);
oa->data = NULL;
return err;
diff --git a/net/sunrpc/xprtrdma/ib_client.c b/net/sunrpc/xprtrdma/ib_client.c
index cfc24e21d260..888638318b6d 100644
--- a/net/sunrpc/xprtrdma/ib_client.c
+++ b/net/sunrpc/xprtrdma/ib_client.c
@@ -52,8 +52,8 @@ static struct rpcrdma_device *rpcrdma_get_client_data(struct ib_device *device)
* is unregistered first.
*
* On failure, a negative errno is returned. rn->rn_done is left
- * NULL on every failure path (it is assigned only after xa_alloc
- * and kref_get have both succeeded), so the @rn may safely be
+ * NULL on every failure path (it is armed before xa_alloc but
+ * cleared again if xa_alloc fails), so the @rn may safely be
* passed to rpcrdma_rn_unregister() without a separate
* registered/unregistered flag in the caller.
*/
@@ -66,10 +66,21 @@ int rpcrdma_rn_register(struct ib_device *device,
if (!rd || test_bit(RPCRDMA_RD_F_REMOVING, &rd->rd_flags))
return -ENETUNREACH;
- if (xa_alloc(&rd->rd_xa, &rn->rn_index, rn, xa_limit_32b, GFP_KERNEL) < 0)
+ /*
+ * Arm rn_done before xa_alloc() publishes @rn: once @rn is
+ * visible in rd_xa, a concurrent rpcrdma_remove_one() can
+ * call rn->rn_done(), so the pointer must already be set.
+ *
+ * Restore NULL if xa_alloc() fails. rn_done doubles as the
+ * registration sentinel for rpcrdma_rn_unregister(); a stale
+ * value would unregister an @rn that was never inserted.
+ */
+ rn->rn_done = done;
+ if (xa_alloc(&rd->rd_xa, &rn->rn_index, rn, xa_limit_32b, GFP_KERNEL) < 0) {
+ rn->rn_done = NULL;
return -ENOMEM;
+ }
kref_get(&rd->rd_kref);
- rn->rn_done = done;
trace_rpcrdma_client_register(device, rn);
return 0;
}
@@ -102,8 +113,9 @@ void rpcrdma_rn_unregister(struct ib_device *device,
/*
* rn_done is the registration sentinel: rpcrdma_rn_register
- * assigns it last, after xa_alloc and kref_get have both
- * succeeded. A NULL rn_done means this notification was
+ * leaves it NULL on every failure path, clearing it again if
+ * xa_alloc fails, so a non-NULL rn_done marks a completed
+ * registration. A NULL rn_done means this notification was
* never registered (or its registration failed) or has
* already been unregistered, and the call is a no-op.
* Without this guard, rn_index == 0 from a kzalloc'd
diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
index 68a0aa627455..1075bfd9fed6 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
@@ -430,11 +430,14 @@ static void svc_rdma_build_arg_xdr(struct svc_rqst *rqstp,
* to the first byte past the Read list. rc_read_pcl and
* rc_call_pcl cl_count fields are set to the number of
* Read segments in the list.
- * %false: Read list is corrupt. @rctxt's xdr_stream is left in an
- * unknown state.
+ * %false: Read list is corrupt or exceeds the page budget. @rctxt's
+ * xdr_stream is left in an unknown state.
*/
static bool xdr_count_read_segments(struct svc_rdma_recv_ctxt *rctxt, __be32 *p)
{
+ unsigned int maxlen = rctxt->rc_maxpages << PAGE_SHIFT;
+ unsigned int total_len = 0;
+
rctxt->rc_call_pcl.cl_count = 0;
rctxt->rc_read_pcl.cl_count = 0;
while (xdr_item_is_present(p)) {
@@ -448,6 +451,11 @@ static bool xdr_count_read_segments(struct svc_rdma_recv_ctxt *rctxt, __be32 *p)
xdr_decode_read_segment(p, &position, &handle,
&length, &offset);
+ if (length > maxlen)
+ return false;
+ total_len += length;
+ if (PAGE_ALIGN(total_len) > maxlen)
+ return false;
if (position) {
if (position & 3)
return false;
diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c
index b3700c1478ea..626e79a31767 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_rw.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c
@@ -766,7 +766,7 @@ static int svc_rdma_build_read_segment(struct svc_rqst *rqstp,
len -= seg_len;
if (len && ((head->rc_curpage + 1) > rqstp->rq_maxpages))
- goto out_overrun;
+ goto out_put;
}
ret = svc_rdma_rw_ctx_init(rdma, ctxt, segment->rs_offset,
@@ -779,7 +779,8 @@ static int svc_rdma_build_read_segment(struct svc_rqst *rqstp,
cc->cc_sqecount += ret;
return 0;
-out_overrun:
+out_put:
+ svc_rdma_put_rw_ctxt(rdma, ctxt);
trace_svcrdma_page_overrun_err(&cc->cc_cid, head->rc_curpage);
return -EINVAL;
}
diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c
index c8d994272f6f..55f810a22c6a 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_transport.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c
@@ -613,11 +613,22 @@ static void svc_rdma_detach(struct svc_xprt *xprt)
rdma_disconnect(rdma->sc_cm_id);
}
-static void __svc_rdma_free(struct work_struct *work)
+/**
+ * svc_rdma_free - Release class-specific transport resources
+ * @xprt: Generic svc transport object
+ */
+static void svc_rdma_free(struct svc_xprt *xprt)
{
struct svcxprt_rdma *rdma =
- container_of(work, struct svcxprt_rdma, sc_work);
- struct ib_device *device = rdma->sc_cm_id->device;
+ container_of(xprt, struct svcxprt_rdma, sc_xprt);
+ struct ib_device *device;
+
+ might_sleep();
+
+ if (!rdma->sc_cm_id)
+ goto out_free;
+
+ device = rdma->sc_cm_id->device;
/* This blocks until the Completion Queues are empty */
if (rdma->sc_qp && !IS_ERR(rdma->sc_qp))
@@ -643,21 +654,14 @@ static void __svc_rdma_free(struct work_struct *work)
if (rdma->sc_pd && !IS_ERR(rdma->sc_pd))
ib_dealloc_pd(rdma->sc_pd);
- /* Destroy the CM ID */
- rdma_destroy_id(rdma->sc_cm_id);
-
if (!test_bit(XPT_LISTENER, &rdma->sc_xprt.xpt_flags))
rpcrdma_rn_unregister(device, &rdma->sc_rn);
- kfree(rdma);
-}
-static void svc_rdma_free(struct svc_xprt *xprt)
-{
- struct svcxprt_rdma *rdma =
- container_of(xprt, struct svcxprt_rdma, sc_xprt);
+ /* Destroy the CM ID */
+ rdma_destroy_id(rdma->sc_cm_id);
- INIT_WORK(&rdma->sc_work, __svc_rdma_free);
- schedule_work(&rdma->sc_work);
+out_free:
+ kfree(rdma);
}
static int svc_rdma_has_wspace(struct svc_xprt *xprt)