From a4eb1e6b98f6246af5f37ba5bf48b8f8a1a54224 Mon Sep 17 00:00:00 2001 From: Dairui Zhang Date: Tue, 29 Sep 2026 03:14:22 +0800 Subject: [PATCH v3] nfsd: fix READ payload landing one page ahead of the XDR accounting nfsd4_encode_readv() computes the in-page offset base = page_len & ~PAGE_MASK and passes it to nfsd_iter_read(), which writes the file data at that offset into *rq_next_page. After each operation rq_next_page is set to page_ptr + 1, so when the reply stream is mid-page the data lands one page ahead of where the payload accounting continues (in the current page at base). The client is then sent the stale tail of the current page - previous RPC payloads, since reply pages are reused without zeroing - instead of the file data: an info leak and corrupted READ results. Trigger: any compound where a READ follows an op that left the stream mid-page, e.g. [PUTFH, READ(100), READ(N)] or READDIR+READ. Place the data in the page the accounting is filling, buf->pages[page_len >> PAGE_SHIFT], instead of one page ahead. At an exact page boundary page_ptr is the empty page after the last full one and page_ptr + 1 is one page too far, so this covers that case too. Fixes: 0d32a6bbb8e7bf ("NFSD: Fix zero NFSv4 READ results when RQ_SPLICE_OK is not set") Suggested-by: Chuck Lever Reported-by: Dairui Zhang Assisted-by: LLM Cc: stable@vger.kernel.org Signed-off-by: Dairui Zhang --- v1 -> v2: - Use buf->pages[page_len >> PAGE_SHIFT] for rq_next_page instead of page_ptr + 1, per Chuck Lever's review. The if (base) guard in v1 only covered the mid-page case. - v1: https://lore.kernel.org/linux-nfs/20260927094535.1745224-1-zhangdairui@gmail.com/ v2 -> v3: - Drop the comment above the new assignment, per Chuck Lever's review. No functional change from v2. - Correct the Fixes tag: the bug in this form starts with 0d32a6bbb8e7bf (v6.6), when base was hoisted before the reserve. The earlier rq_vec form is not affected. Tested on 7.2.6 in a QEMU guest. The test file tags every 4K page with its index. A raw COMPOUND{PUTFH, READ(0,100), READ(4096,4096)} forces the readv path (readcount=2); ftrace confirmed nfsd_iter_read for both READs. Unpatched, the second READ returns stale page contents instead of the file data (heap bytes in one run, zeros in another). The first READ only looked correct because the stale page happened to hold the same page-0 content. Patched, both READs return the expected bytes. A localhost v4.1 mount with 128K/1M dd reads (splice path) gives identical md5 before and after. - v2: https://lore.kernel.org/linux-nfs/20260928182749.1835924-1-zhangdairui@gmail.com/ --- fs/nfsd/nfs4xdr.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -4933,6 +4933,9 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp, __be32 zero = xdr_zero; __be32 nfserr; + resp->rqstp->rq_next_page = xdr->buf->pages + + (xdr->buf->page_len >> PAGE_SHIFT); + nfserr = nfsd_iter_read(resp->rqstp, read->rd_fhp, read->rd_nf, read->rd_offset, &maxcount, base, &read->rd_eof); -- 2.53.0