SUNRPC: Fix callback channel

The NFSv4.1 callback channel is currently broken because the receive
message will keep shrinking because the backchannel receive buffer size
never gets reset.
The easiest solution to this problem is instead of changing the receive
buffer, to rather adjust the copied request.

Fixes: 38b7631fbe42 ("nfs4: limit callback decoding to received bytes")
Cc: Benjamin Coddington <bcodding@redhat.com>
Cc: stable@vger.kernel.org
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>

Changed files
+14 -13
fs
net
+2 -5
fs/nfs/callback_xdr.c
··· 78 78 79 79 p = xdr_inline_decode(xdr, nbytes); 80 80 if (unlikely(p == NULL)) 81 - printk(KERN_WARNING "NFS: NFSv4 callback reply buffer overflowed " 82 - "or truncated request.\n"); 81 + printk(KERN_WARNING "NFS: NFSv4 callback reply buffer overflowed!\n"); 83 82 return p; 84 83 } 85 84 ··· 889 890 struct cb_compound_hdr_arg hdr_arg = { 0 }; 890 891 struct cb_compound_hdr_res hdr_res = { NULL }; 891 892 struct xdr_stream xdr_in, xdr_out; 892 - struct xdr_buf *rq_arg = &rqstp->rq_arg; 893 893 __be32 *p, status; 894 894 struct cb_process_state cps = { 895 895 .drc_status = 0, ··· 900 902 901 903 dprintk("%s: start\n", __func__); 902 904 903 - rq_arg->len = rq_arg->head[0].iov_len + rq_arg->page_len; 904 - xdr_init_decode(&xdr_in, rq_arg, rq_arg->head[0].iov_base); 905 + xdr_init_decode(&xdr_in, &rqstp->rq_arg, rqstp->rq_arg.head[0].iov_base); 905 906 906 907 p = (__be32*)((char *)rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len); 907 908 xdr_init_encode(&xdr_out, &rqstp->rq_res, p);
-8
net/sunrpc/backchannel_rqst.c
··· 353 353 { 354 354 struct rpc_xprt *xprt = req->rq_xprt; 355 355 struct svc_serv *bc_serv = xprt->bc_serv; 356 - struct xdr_buf *rq_rcv_buf = &req->rq_rcv_buf; 357 356 358 357 spin_lock(&xprt->bc_pa_lock); 359 358 list_del(&req->rq_bc_pa_list); 360 359 xprt_dec_alloc_count(xprt, 1); 361 360 spin_unlock(&xprt->bc_pa_lock); 362 - 363 - if (copied <= rq_rcv_buf->head[0].iov_len) { 364 - rq_rcv_buf->head[0].iov_len = copied; 365 - rq_rcv_buf->page_len = 0; 366 - } else { 367 - rq_rcv_buf->page_len = copied - rq_rcv_buf->head[0].iov_len; 368 - } 369 361 370 362 req->rq_private_buf.len = copied; 371 363 set_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state);
+12
net/sunrpc/svc.c
··· 1363 1363 memcpy(&rqstp->rq_addr, &req->rq_xprt->addr, rqstp->rq_addrlen); 1364 1364 memcpy(&rqstp->rq_arg, &req->rq_rcv_buf, sizeof(rqstp->rq_arg)); 1365 1365 memcpy(&rqstp->rq_res, &req->rq_snd_buf, sizeof(rqstp->rq_res)); 1366 + 1367 + /* Adjust the argument buffer length */ 1366 1368 rqstp->rq_arg.len = req->rq_private_buf.len; 1369 + if (rqstp->rq_arg.len <= rqstp->rq_arg.head[0].iov_len) { 1370 + rqstp->rq_arg.head[0].iov_len = rqstp->rq_arg.len; 1371 + rqstp->rq_arg.page_len = 0; 1372 + } else if (rqstp->rq_arg.len <= rqstp->rq_arg.head[0].iov_len + 1373 + rqstp->rq_arg.page_len) 1374 + rqstp->rq_arg.page_len = rqstp->rq_arg.len - 1375 + rqstp->rq_arg.head[0].iov_len; 1376 + else 1377 + rqstp->rq_arg.len = rqstp->rq_arg.head[0].iov_len + 1378 + rqstp->rq_arg.page_len; 1367 1379 1368 1380 /* reset result send buffer "put" position */ 1369 1381 resv->iov_len = 0;