Commit 96f8778f authored by Chuck Lever's avatar Chuck Lever Committed by Anna Schumaker

xprtrdma: Add xdr_init_decode to rpcrdma_reply_handler()

Transport header decoding deals with untrusted input data, therefore
decoding this header needs to be hardened.

Adopt the same infrastructure that is used when XDR decoding NFS
replies. This is slightly more CPU-intensive than the replaced code,
but we're not adding new atomics, locking, or context switches. The
cost is manageable.

Start by initializing an xdr_stream in rpcrdma_reply_handler().
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>
Signed-off-by: default avatarAnna Schumaker <Anna.Schumaker@Netapp.com>
parent d31ae254
...@@ -993,10 +993,11 @@ rpcrdma_reply_handler(struct work_struct *work) ...@@ -993,10 +993,11 @@ rpcrdma_reply_handler(struct work_struct *work)
struct rpcrdma_xprt *r_xprt = rep->rr_rxprt; struct rpcrdma_xprt *r_xprt = rep->rr_rxprt;
struct rpcrdma_buffer *buf = &r_xprt->rx_buf; struct rpcrdma_buffer *buf = &r_xprt->rx_buf;
struct rpc_xprt *xprt = &r_xprt->rx_xprt; struct rpc_xprt *xprt = &r_xprt->rx_xprt;
struct xdr_stream *xdr = &rep->rr_stream;
struct rpcrdma_msg *headerp; struct rpcrdma_msg *headerp;
struct rpcrdma_req *req; struct rpcrdma_req *req;
struct rpc_rqst *rqst; struct rpc_rqst *rqst;
__be32 *iptr; __be32 *iptr, *p, xid, vers, proc;
int rdmalen, status, rmerr; int rdmalen, status, rmerr;
unsigned long cwnd; unsigned long cwnd;
struct list_head mws; struct list_head mws;
...@@ -1005,8 +1006,18 @@ rpcrdma_reply_handler(struct work_struct *work) ...@@ -1005,8 +1006,18 @@ rpcrdma_reply_handler(struct work_struct *work)
if (rep->rr_len == RPCRDMA_BAD_LEN) if (rep->rr_len == RPCRDMA_BAD_LEN)
goto out_badstatus; goto out_badstatus;
if (rep->rr_len < RPCRDMA_HDRLEN_ERR)
xdr_init_decode(xdr, &rep->rr_hdrbuf,
rep->rr_hdrbuf.head[0].iov_base);
/* Fixed transport header fields */
p = xdr_inline_decode(xdr, 4 * sizeof(*p));
if (unlikely(!p))
goto out_shortreply; goto out_shortreply;
xid = *p++;
vers = *p++;
p++; /* credits */
proc = *p++;
headerp = rdmab_to_msg(rep->rr_rdmabuf); headerp = rdmab_to_msg(rep->rr_rdmabuf);
#if defined(CONFIG_SUNRPC_BACKCHANNEL) #if defined(CONFIG_SUNRPC_BACKCHANNEL)
...@@ -1018,8 +1029,7 @@ rpcrdma_reply_handler(struct work_struct *work) ...@@ -1018,8 +1029,7 @@ rpcrdma_reply_handler(struct work_struct *work)
* get context for handling any incoming chunks. * get context for handling any incoming chunks.
*/ */
spin_lock(&buf->rb_lock); spin_lock(&buf->rb_lock);
req = rpcrdma_lookup_req_locked(&r_xprt->rx_buf, req = rpcrdma_lookup_req_locked(&r_xprt->rx_buf, xid);
headerp->rm_xid);
if (!req) if (!req)
goto out_nomatch; goto out_nomatch;
if (req->rl_reply) if (req->rl_reply)
...@@ -1035,7 +1045,7 @@ rpcrdma_reply_handler(struct work_struct *work) ...@@ -1035,7 +1045,7 @@ rpcrdma_reply_handler(struct work_struct *work)
spin_unlock(&buf->rb_lock); spin_unlock(&buf->rb_lock);
dprintk("RPC: %s: reply %p completes request %p (xid 0x%08x)\n", dprintk("RPC: %s: reply %p completes request %p (xid 0x%08x)\n",
__func__, rep, req, be32_to_cpu(headerp->rm_xid)); __func__, rep, req, be32_to_cpu(xid));
/* Invalidate and unmap the data payloads before waking the /* Invalidate and unmap the data payloads before waking the
* waiting application. This guarantees the memory regions * waiting application. This guarantees the memory regions
...@@ -1052,16 +1062,16 @@ rpcrdma_reply_handler(struct work_struct *work) ...@@ -1052,16 +1062,16 @@ rpcrdma_reply_handler(struct work_struct *work)
* the rep, rqst, and rq_task pointers remain stable. * the rep, rqst, and rq_task pointers remain stable.
*/ */
spin_lock_bh(&xprt->transport_lock); spin_lock_bh(&xprt->transport_lock);
rqst = xprt_lookup_rqst(xprt, headerp->rm_xid); rqst = xprt_lookup_rqst(xprt, xid);
if (!rqst) if (!rqst)
goto out_norqst; goto out_norqst;
xprt->reestablish_timeout = 0; xprt->reestablish_timeout = 0;
if (headerp->rm_vers != rpcrdma_version) if (vers != rpcrdma_version)
goto out_badversion; goto out_badversion;
/* check for expected message types */ /* check for expected message types */
/* The order of some of these tests is important. */ /* The order of some of these tests is important. */
switch (headerp->rm_type) { switch (proc) {
case rdma_msg: case rdma_msg:
/* never expect read chunks */ /* never expect read chunks */
/* never expect reply chunks (two ways to check) */ /* never expect reply chunks (two ways to check) */
...@@ -1123,7 +1133,7 @@ rpcrdma_reply_handler(struct work_struct *work) ...@@ -1123,7 +1133,7 @@ rpcrdma_reply_handler(struct work_struct *work)
default: default:
dprintk("RPC: %5u %s: invalid rpcrdma reply (type %u)\n", dprintk("RPC: %5u %s: invalid rpcrdma reply (type %u)\n",
rqst->rq_task->tk_pid, __func__, rqst->rq_task->tk_pid, __func__,
be32_to_cpu(headerp->rm_type)); be32_to_cpu(proc));
status = -EIO; status = -EIO;
r_xprt->rx_stats.bad_reply_count++; r_xprt->rx_stats.bad_reply_count++;
break; break;
...@@ -1161,7 +1171,7 @@ rpcrdma_reply_handler(struct work_struct *work) ...@@ -1161,7 +1171,7 @@ rpcrdma_reply_handler(struct work_struct *work)
*/ */
out_badversion: out_badversion:
dprintk("RPC: %s: invalid version %d\n", dprintk("RPC: %s: invalid version %d\n",
__func__, be32_to_cpu(headerp->rm_vers)); __func__, be32_to_cpu(vers));
status = -EIO; status = -EIO;
r_xprt->rx_stats.bad_reply_count++; r_xprt->rx_stats.bad_reply_count++;
goto out; goto out;
...@@ -1204,16 +1214,15 @@ rpcrdma_reply_handler(struct work_struct *work) ...@@ -1204,16 +1214,15 @@ rpcrdma_reply_handler(struct work_struct *work)
out_nomatch: out_nomatch:
spin_unlock(&buf->rb_lock); spin_unlock(&buf->rb_lock);
dprintk("RPC: %s: no match for incoming xid 0x%08x len %d\n", dprintk("RPC: %s: no match for incoming xid 0x%08x\n",
__func__, be32_to_cpu(headerp->rm_xid), __func__, be32_to_cpu(xid));
rep->rr_len);
goto repost; goto repost;
out_duplicate: out_duplicate:
spin_unlock(&buf->rb_lock); spin_unlock(&buf->rb_lock);
dprintk("RPC: %s: " dprintk("RPC: %s: "
"duplicate reply %p to RPC request %p: xid 0x%08x\n", "duplicate reply %p to RPC request %p: xid 0x%08x\n",
__func__, rep, req, be32_to_cpu(headerp->rm_xid)); __func__, rep, req, be32_to_cpu(xid));
/* If no pending RPC transaction was matched, post a replacement /* If no pending RPC transaction was matched, post a replacement
* receive buffer before returning. * receive buffer before returning.
......
...@@ -180,6 +180,7 @@ rpcrdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc) ...@@ -180,6 +180,7 @@ rpcrdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc)
__func__, rep, wc->byte_len); __func__, rep, wc->byte_len);
rep->rr_len = wc->byte_len; rep->rr_len = wc->byte_len;
rpcrdma_set_xdrlen(&rep->rr_hdrbuf, wc->byte_len);
rep->rr_wc_flags = wc->wc_flags; rep->rr_wc_flags = wc->wc_flags;
rep->rr_inv_rkey = wc->ex.invalidate_rkey; rep->rr_inv_rkey = wc->ex.invalidate_rkey;
...@@ -974,6 +975,8 @@ rpcrdma_create_rep(struct rpcrdma_xprt *r_xprt) ...@@ -974,6 +975,8 @@ rpcrdma_create_rep(struct rpcrdma_xprt *r_xprt)
rc = PTR_ERR(rep->rr_rdmabuf); rc = PTR_ERR(rep->rr_rdmabuf);
goto out_free; goto out_free;
} }
xdr_buf_init(&rep->rr_hdrbuf, rep->rr_rdmabuf->rg_base,
rdmab_length(rep->rr_rdmabuf));
rep->rr_cqe.done = rpcrdma_wc_receive; rep->rr_cqe.done = rpcrdma_wc_receive;
rep->rr_rxprt = r_xprt; rep->rr_rxprt = r_xprt;
......
...@@ -223,6 +223,8 @@ struct rpcrdma_rep { ...@@ -223,6 +223,8 @@ struct rpcrdma_rep {
u32 rr_inv_rkey; u32 rr_inv_rkey;
struct rpcrdma_xprt *rr_rxprt; struct rpcrdma_xprt *rr_rxprt;
struct work_struct rr_work; struct work_struct rr_work;
struct xdr_buf rr_hdrbuf;
struct xdr_stream rr_stream;
struct list_head rr_list; struct list_head rr_list;
struct ib_recv_wr rr_recv_wr; struct ib_recv_wr rr_recv_wr;
struct rpcrdma_regbuf *rr_rdmabuf; struct rpcrdma_regbuf *rr_rdmabuf;
...@@ -642,6 +644,12 @@ int rpcrdma_marshal_req(struct rpc_rqst *); ...@@ -642,6 +644,12 @@ int rpcrdma_marshal_req(struct rpc_rqst *);
void rpcrdma_set_max_header_sizes(struct rpcrdma_xprt *); void rpcrdma_set_max_header_sizes(struct rpcrdma_xprt *);
void rpcrdma_reply_handler(struct work_struct *work); void rpcrdma_reply_handler(struct work_struct *work);
static inline void rpcrdma_set_xdrlen(struct xdr_buf *xdr, size_t len)
{
xdr->head[0].iov_len = len;
xdr->len = len;
}
/* RPC/RDMA module init - xprtrdma/transport.c /* RPC/RDMA module init - xprtrdma/transport.c
*/ */
extern unsigned int xprt_rdma_max_inline_read; extern unsigned int xprt_rdma_max_inline_read;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment