mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-09-18 23:09:29 +02:00
nfsd: fix XDR length calculation in nfsd4_ff_encode_layoutget
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: 9b9960a0ca ("nfsd: Add a super simple flex file server")
Cc: stable@vger.kernel.org
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
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user