[PATCH] nfsd: fix XDR length calculation in nfsd4_ff_encode_layoutget

Jeff Layton posted 1 patch 1 week, 3 days ago
fs/nfsd/flexfilelayoutxdr.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
[PATCH] nfsd: fix XDR length calculation in nfsd4_ff_encode_layoutget
Posted by Jeff Layton 1 week, 3 days ago
The XDR buffer size calculation in nfsd4_ff_encode_layoutget() has
multiple errors that can result in either an out-of-bounds write or
leaking uninitialized kernel memory to the client:

 - fh_len doesn't account for XDR padding on the file handle data
 - uid and gid lengths use "8 + len" but xdr_encode_opaque() actually
   writes "4 + xdr_align_size(len)" bytes
 - ds_len omits the flags and stats_collect_hint fields (8 bytes),
   while len's header constant overestimates by 8 bytes -- these
   partially cancel but leave a net mismatch

The worst case occurs with short strings (e.g. uid=0, gid=0 with an
odd-sized file handle), where the function writes up to 5 bytes past
the reserved XDR buffer. Conversely, when string lengths happen to be
4-byte aligned, the reservation is too large and stale buffer content
is sent to the client.

Fix this by breaking out every encoded field explicitly in the ds_len
calculation, using xdr_align_size() for all variable-length opaque
fields, and correcting the header constants.

Fixes: 9b9960a0ca47 ("nfsd: Add a super simple flex file server")
Assisted-by: kres:claude-opus-4-7
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
 fs/nfsd/flexfilelayoutxdr.c | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/fs/nfsd/flexfilelayoutxdr.c b/fs/nfsd/flexfilelayoutxdr.c
index f9f7e38cba13..95b7957a34d9 100644
--- a/fs/nfsd/flexfilelayoutxdr.c
+++ b/fs/nfsd/flexfilelayoutxdr.c
@@ -30,19 +30,24 @@ nfsd4_ff_encode_layoutget(struct xdr_stream *xdr,
 	struct ff_idmap uid;
 	struct ff_idmap gid;
 
-	fh_len = 4 + fl->fh.size;
+	fh_len = 4 + xdr_align_size(fl->fh.size);
 
 	uid.len = sprintf(uid.buf, "%u", from_kuid(&init_user_ns, fl->uid));
 	gid.len = sprintf(gid.buf, "%u", from_kgid(&init_user_ns, fl->gid));
 
-	/* 8 + len for recording the length, name, and padding */
-	ds_len = 20 + sizeof(stateid_opaque_t) + 4 + fh_len +
-		 8 + uid.len + 8 + gid.len;
+	/* data server entry: deviceid + efficiency + stateid + fh list +
+	 * user + group + flags + stats_collect_hint
+	 */
+	ds_len = 16 + 4 + 4 + sizeof(stateid_opaque_t) + 4 + fh_len +
+		 4 + xdr_align_size(uid.len) +
+		 4 + xdr_align_size(gid.len) +
+		 4 + 4;
 
+	/* mirror: ds_count + ds */
 	mirror_len = 4 + ds_len;
 
-	/* The layout segment */
-	len = 20 + mirror_len;
+	/* stripe_unit + mirror_count + mirror */
+	len = 12 + mirror_len;
 
 	p = xdr_reserve_space(xdr, sizeof(__be32) + len);
 	if (!p)

---
base-commit: eb97e6388ea32148006bb88ebdb06a0b9ba78cd1
change-id: 20260528-pnfs-fixes-4d551f83a5f8

Best regards,
-- 
Jeff Layton <jlayton@kernel.org>
Re: [PATCH] nfsd: fix XDR length calculation in nfsd4_ff_encode_layoutget
Posted by Chuck Lever 1 week, 3 days ago
From: Chuck Lever <chuck.lever@oracle.com>

On Thu, 28 May 2026 10:38:15 -0400, Jeff Layton wrote:
> The XDR buffer size calculation in nfsd4_ff_encode_layoutget() has
> multiple errors that can result in either an out-of-bounds write or
> leaking uninitialized kernel memory to the client:
> 
>  - fh_len doesn't account for XDR padding on the file handle data
>  - uid and gid lengths use "8 + len" but xdr_encode_opaque() actually
>    writes "4 + xdr_align_size(len)" bytes
>  - ds_len omits the flags and stats_collect_hint fields (8 bytes),
>    while len's header constant overestimates by 8 bytes -- these
>    partially cancel but leave a net mismatch
> 
> [...]

Applied to nfsd-testing, thanks!

[1/1] nfsd: fix XDR length calculation in nfsd4_ff_encode_layoutget
      commit: c8bb9d5360bfb534065aed091579199ad2843f43

--
Chuck Lever <chuck.lever@oracle.com>