6.1-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jeff Layton <jlayton@kernel.org>
commit f9868174af49d207fbaf0c5e055d088a983684af upstream.
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")
Cc: <redacted>
Assisted-by: kres:claude-opus-4-7
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20260528-pnfs-fixes-v1-1-8a1255ae2f16@kernel.org (local)
Signed-off-by: Chuck Lever <redacted>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
fs/nfsd/flexfilelayoutxdr.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
--- a/fs/nfsd/flexfilelayoutxdr.c
+++ b/fs/nfsd/flexfilelayoutxdr.c
@@ -30,19 +30,24 @@ nfsd4_ff_encode_layoutget(struct xdr_str
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)