forked from mirrors/linux
		
	nfs4: limit callback decoding to received bytes
A truncated cb_compound request will cause the client to decode null or data from a previous callback for nfs4.1 backchannel case, or uninitialized data for the nfs4.0 case. This is because the path through svc_process_common() advances the request's iov_base and decrements iov_len without adjusting the overall xdr_buf's len field. That causes xdr_init_decode() to set up the xdr_stream with an incorrect length in nfs4_callback_compound(). Fixing this for the nfs4.1 backchannel case first requires setting the correct iov_len and page_len based on the length of received data in the same manner as the nfs4.0 case. Then the request's xdr_buf length can be adjusted for both cases based upon the remaining iov_len and page_len. Signed-off-by: Benjamin Coddington <bcodding@redhat.com> Cc: stable@vger.kernel.org Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
This commit is contained in:
		
							parent
							
								
									c68a027c05
								
							
						
					
					
						commit
						38b7631fbe
					
				
					 3 changed files with 14 additions and 2 deletions
				
			
		| 
						 | 
					@ -78,7 +78,8 @@ static __be32 *read_buf(struct xdr_stream *xdr, int nbytes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	p = xdr_inline_decode(xdr, nbytes);
 | 
						p = xdr_inline_decode(xdr, nbytes);
 | 
				
			||||||
	if (unlikely(p == NULL))
 | 
						if (unlikely(p == NULL))
 | 
				
			||||||
		printk(KERN_WARNING "NFS: NFSv4 callback reply buffer overflowed!\n");
 | 
							printk(KERN_WARNING "NFS: NFSv4 callback reply buffer overflowed "
 | 
				
			||||||
 | 
												"or truncated request.\n");
 | 
				
			||||||
	return p;
 | 
						return p;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -889,6 +890,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
 | 
				
			||||||
	struct cb_compound_hdr_arg hdr_arg = { 0 };
 | 
						struct cb_compound_hdr_arg hdr_arg = { 0 };
 | 
				
			||||||
	struct cb_compound_hdr_res hdr_res = { NULL };
 | 
						struct cb_compound_hdr_res hdr_res = { NULL };
 | 
				
			||||||
	struct xdr_stream xdr_in, xdr_out;
 | 
						struct xdr_stream xdr_in, xdr_out;
 | 
				
			||||||
 | 
						struct xdr_buf *rq_arg = &rqstp->rq_arg;
 | 
				
			||||||
	__be32 *p, status;
 | 
						__be32 *p, status;
 | 
				
			||||||
	struct cb_process_state cps = {
 | 
						struct cb_process_state cps = {
 | 
				
			||||||
		.drc_status = 0,
 | 
							.drc_status = 0,
 | 
				
			||||||
| 
						 | 
					@ -900,7 +902,8 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dprintk("%s: start\n", __func__);
 | 
						dprintk("%s: start\n", __func__);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	xdr_init_decode(&xdr_in, &rqstp->rq_arg, rqstp->rq_arg.head[0].iov_base);
 | 
						rq_arg->len = rq_arg->head[0].iov_len + rq_arg->page_len;
 | 
				
			||||||
 | 
						xdr_init_decode(&xdr_in, rq_arg, rq_arg->head[0].iov_base);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	p = (__be32*)((char *)rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len);
 | 
						p = (__be32*)((char *)rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len);
 | 
				
			||||||
	xdr_init_encode(&xdr_out, &rqstp->rq_res, p);
 | 
						xdr_init_encode(&xdr_out, &rqstp->rq_res, p);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -353,12 +353,20 @@ void xprt_complete_bc_request(struct rpc_rqst *req, uint32_t copied)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct rpc_xprt *xprt = req->rq_xprt;
 | 
						struct rpc_xprt *xprt = req->rq_xprt;
 | 
				
			||||||
	struct svc_serv *bc_serv = xprt->bc_serv;
 | 
						struct svc_serv *bc_serv = xprt->bc_serv;
 | 
				
			||||||
 | 
						struct xdr_buf *rq_rcv_buf = &req->rq_rcv_buf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spin_lock(&xprt->bc_pa_lock);
 | 
						spin_lock(&xprt->bc_pa_lock);
 | 
				
			||||||
	list_del(&req->rq_bc_pa_list);
 | 
						list_del(&req->rq_bc_pa_list);
 | 
				
			||||||
	xprt_dec_alloc_count(xprt, 1);
 | 
						xprt_dec_alloc_count(xprt, 1);
 | 
				
			||||||
	spin_unlock(&xprt->bc_pa_lock);
 | 
						spin_unlock(&xprt->bc_pa_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (copied <= rq_rcv_buf->head[0].iov_len) {
 | 
				
			||||||
 | 
							rq_rcv_buf->head[0].iov_len = copied;
 | 
				
			||||||
 | 
							rq_rcv_buf->page_len = 0;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							rq_rcv_buf->page_len = copied - rq_rcv_buf->head[0].iov_len;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req->rq_private_buf.len = copied;
 | 
						req->rq_private_buf.len = copied;
 | 
				
			||||||
	set_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state);
 | 
						set_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1363,6 +1363,7 @@ bc_svc_process(struct svc_serv *serv, struct rpc_rqst *req,
 | 
				
			||||||
	memcpy(&rqstp->rq_addr, &req->rq_xprt->addr, rqstp->rq_addrlen);
 | 
						memcpy(&rqstp->rq_addr, &req->rq_xprt->addr, rqstp->rq_addrlen);
 | 
				
			||||||
	memcpy(&rqstp->rq_arg, &req->rq_rcv_buf, sizeof(rqstp->rq_arg));
 | 
						memcpy(&rqstp->rq_arg, &req->rq_rcv_buf, sizeof(rqstp->rq_arg));
 | 
				
			||||||
	memcpy(&rqstp->rq_res, &req->rq_snd_buf, sizeof(rqstp->rq_res));
 | 
						memcpy(&rqstp->rq_res, &req->rq_snd_buf, sizeof(rqstp->rq_res));
 | 
				
			||||||
 | 
						rqstp->rq_arg.len = req->rq_private_buf.len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* reset result send buffer "put" position */
 | 
						/* reset result send buffer "put" position */
 | 
				
			||||||
	resv->iov_len = 0;
 | 
						resv->iov_len = 0;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue