Re: [PATCH] nfsd: truncate LISTXATTRS replies on error

From: Jeff Layton

Date: Wed Oct 21 2026 - 11:17:37 EST


On Wed, 2026-10-21 at 11:37 +0900, Hyunsol Mun wrote:
> nfsd4_encode_listxattrs() reserves 12 bytes for the reply cookie and
> array count before it starts encoding attribute names. If the first user
> attribute name does not fit in the client's maxcount, the encoder returns
> NFS4ERR_TOOSMALL without initializing the reservation.
>
> The generic COMPOUND encoder does not truncate replies for this error, so
> the reserved bytes are transmitted after the operation status and can
> disclose stale reply-buffer contents.
>
> Save the XDR length on entry and restore it whenever the LISTXATTRS
> encoder returns an error. This also discards partial output from other
> errors after encoding has started.
>
> Validation used nfsd-testing b4e06bef3884. On the unpatched x86_64
> kernel, all 1,000 NFS4ERR_TOOSMALL replies contained the extra 12-byte
> body. The leaked windows reconstructed another client's exact session ID
> and enabled cached OPEN replay, stateid use, creation of a new session
> for the victim client, and callback-state modification.
>
> With this patch, two runs totaling 2,000 NFS4ERR_TOOSMALL replies
> contained no extra body or leak window, so session reconstruction
> failed. The victim's cached OPEN completed normally in both runs.
>
> A patched-kernel smoke run also completed a successful LISTXATTRS
> through an NFSv4.2 mount. NFSv4.2 and NFSv3 read/write/unmount tests
> passed. All test kernels built with GCC 13.3 and CONFIG_WERROR, and no
> kernel warning, Oops, or panic occurred. The reproducer and complete
> logs are available privately on request.
>
> The vulnerability research and validation were conducted by members of
> the Tobabz team as part of the Best of the Best 15th program.
>
> Fixes: 23e50fe3a5e6 ("nfsd: implement the xattr functions and en/decode logic")
> Cc: stable@xxxxxxxxxxxxxxx
> Assisted-by: LLM
> Signed-off-by: Hyunsol Mun <muumthf@xxxxxxxxx>
> ---
> fs/nfsd/nfs4xdr.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> index c4fd840da4d9..c7c2be555b59 100644
> --- a/fs/nfsd/nfs4xdr.c
> +++ b/fs/nfsd/nfs4xdr.c
> @@ -6438,6 +6438,7 @@ nfsd4_encode_listxattrs(struct nfsd4_compoundres *resp, __be32 nfserr,
> {
> struct nfsd4_listxattrs *listxattrs = &u->listxattrs;
> struct xdr_stream *xdr = resp->xdr;
> + unsigned int starting_len = xdr->buf->len;
> u32 cookie_offset, count_offset, eof;
> u32 left, xdrleft, slen, count;
> u32 xdrlen, offset;
> @@ -6545,6 +6546,8 @@ nfsd4_encode_listxattrs(struct nfsd4_compoundres *resp, __be32 nfserr,
> out:
> if (listxattrs->lsxa_len)
> kvfree(listxattrs->lsxa_buf);
> + if (status)
> + xdr_truncate_encode(xdr, starting_len);
> return status;
> }
>
>
> base-commit: b4e06bef38843c8b909d0b4317dfaaf87dba6d72

Reviewed-by: Jeff Layton <jlayton@xxxxxxxxxx>