From 0f4a767340fad392bd656115b8752005518c9065 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Thu, 11 Jun 2026 16:00:50 -0400 Subject: [PATCH] nfsd: validate nseconds in TIME_DELEG decode paths The xdrgen-based TIME_DELEG_ACCESS and TIME_DELEG_MODIFY decode arms store a raw uint32_t nseconds directly into tv_nsec without enforcing nseconds < NSEC_PER_SEC. The legacy nfsd4_decode_nfstime4 has this check but the TIME_DELEG paths do not. A malformed timespec can propagate through notify_change() to disk. Add range checks in both nfs4xdr.c (SETATTR path) and nfs4callback.c (CB_GETATTR path). Fixes: 6ae30d6eb26b ("nfsd: add support for delegated timestamps") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Jeff Layton Link: https://patch.msgid.link/20260611-nfsd-testing-v2-7-5b90e276f2d9@kernel.org Signed-off-by: Chuck Lever --- fs/nfsd/nfs4callback.c | 4 ++++ fs/nfsd/nfs4xdr.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 1628bb9ef9dd..7c868afc329e 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -108,6 +108,8 @@ static int decode_cb_fattr4(struct xdr_stream *xdr, uint32_t *bitmap, if (!xdrgen_decode_fattr4_time_deleg_access(xdr, &access)) return -EIO; + if (access.nseconds >= NSEC_PER_SEC) + return -EIO; fattr->ncf_cb_atime.tv_sec = access.seconds; fattr->ncf_cb_atime.tv_nsec = access.nseconds; @@ -117,6 +119,8 @@ static int decode_cb_fattr4(struct xdr_stream *xdr, uint32_t *bitmap, if (!xdrgen_decode_fattr4_time_deleg_modify(xdr, &modify)) return -EIO; + if (modify.nseconds >= NSEC_PER_SEC) + return -EIO; fattr->ncf_cb_mtime.tv_sec = modify.seconds; fattr->ncf_cb_mtime.tv_nsec = modify.nseconds; diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 6f356c71e234..ad192d25724c 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -637,6 +637,8 @@ nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen, if (!xdrgen_decode_fattr4_time_deleg_access(argp->xdr, &access)) return nfserr_bad_xdr; + if (access.nseconds >= NSEC_PER_SEC) + return nfserr_inval; iattr->ia_atime.tv_sec = access.seconds; iattr->ia_atime.tv_nsec = access.nseconds; iattr->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET | ATTR_DELEG; @@ -646,6 +648,8 @@ nfsd4_decode_fattr4(struct nfsd4_compoundargs *argp, u32 *bmval, u32 bmlen, if (!xdrgen_decode_fattr4_time_deleg_modify(argp->xdr, &modify)) return nfserr_bad_xdr; + if (modify.nseconds >= NSEC_PER_SEC) + return nfserr_inval; iattr->ia_mtime.tv_sec = modify.seconds; iattr->ia_mtime.tv_nsec = modify.nseconds; iattr->ia_ctime.tv_sec = modify.seconds;