From a0db57fee35ece5603f852df1b665e01e0556f65 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Wed, 26 Aug 2026 10:34:15 -0400 Subject: [PATCH] xprtrdma: Fix Write chunk payload corruption on a short reply header An NFSv3 server may return READ3resok with attributes_follow set to FALSE. The client has already reserved enough head iovec space for the largest possible reply header, so such a reply falls 84 bytes short of that estimate. rpcrdma_inline_fixup() points the head iovec at the received message but leaves its length at the reserved size, so it runs past the header into stale receive buffer content. xdr_realign_pages() then shifts those bytes into the page list, and the READ returns transport data where READ payload belongs. nfs3_read_done() records the shorter header size, so on an ordinary NFSv3 mount, only the first READ is corrupted. However, the FlexFiles layout driver installs its own completion callback, so every READ from a data server is corrupt. To address this, reduce the head iovec to the header bytes actually received when a Write chunk carries the READ payload. An auth flavor that transforms the payload cannot use a Write chunk, so scoping it to Write chunks leaves krb5[ip] alone. Fixes: cb0ae1fbb2f5 ("xprtrdma: Do not update {head, tail}.iov_len in rpcrdma_inline_fixup()") Reported-by: Rakesh Bhargava Closes: https://lore.kernel.org/linux-nfs/SA6PR06MB104695205126871E29444567A8BA02@SA6PR06MB10469.namprd06.prod.outlook.com/ Signed-off-by: Chuck Lever Signed-off-by: Trond Myklebust --- net/sunrpc/xprtrdma/rpc_rdma.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index 1285f04cdac1..ba8bdca8a49f 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -1243,9 +1243,19 @@ rpcrdma_decode_msg(struct rpcrdma_xprt *r_xprt, struct rpcrdma_rep *rep, /* Build the RPC reply's Payload stream in rqst->rq_rcv_buf */ base = (char *)xdr_inline_decode(xdr, 0); - rpclen = xdr_stream_remaining(xdr); + rpclen = rep->rr_hdrbuf.len - xdr_stream_pos(xdr); + if (writelist) + rpclen &= ~3; + if (unlikely(!rpclen)) + return -EIO; + if (writelist && rpclen < rqst->rq_rcv_buf.head[0].iov_len) { + rqst->rq_rcv_buf.head[0].iov_len = rpclen; + rqst->rq_private_buf.head[0].iov_len = rpclen; + } + r_xprt->rx_stats.fixup_copy_count += - rpcrdma_inline_fixup(rqst, base, rpclen, writelist & 3); + rpcrdma_inline_fixup(rqst, base, rpclen, + xdr_pad_size(writelist)); r_xprt->rx_stats.total_rdma_reply += writelist; return rpclen + xdr_align_size(writelist);