[PATCH OLK-5.10] nfsd: reject out-of-range useconds in NFSv2 SETATTR/CREATE
From: Robbie Ko <robbieko@synology.com> stable inclusion from stable-v5.10.270 commit cdc3299f7072777b09c9582a3b8b65bb163ddcf3 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/18931 CVE: CVE-2026-89665 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- commit 26709c8ffe73772eb69e68d553ac71d91228dccc upstream. The NFSv2 sattr decoder converts the wire useconds to nanoseconds in svcxdr_decode_sattr(): iap->ia_atime.tv_nsec = tmp2 * NSEC_PER_USEC; tmp2 is a u32 and NSEC_PER_USEC is 1000, so the product is computed in unsigned long. On ILP32 that is 32 bits, and an out-of-range useconds value such as 4294968 wraps to tv_nsec == 704. The corruption therefore happens during decode, before any proc function can inspect the value, and a later range check on tv_nsec would see an in-range result and accept it. Rejecting in the decoder yields an RPC GARBAGE_ARGS reply. NFSv2 defines no NFSERR_INVAL, so there is no NFS-level status to return for a malformed time argument, and the check cannot move to the proc function the way the v3/v4 nsec range checks do. Guard the raw useconds before the multiplication and reject values greater than 1000000. useconds == 1000000 is kept: it is the Sun convention for "set to the current server time", and the in-tree Linux NFSv2 client emits it in both the atime and the mtime field for a plain touch / utimes(file, NULL) (see encode_sattr() and xdr_encode_current_server_time() in fs/nfs/nfs2xdr.c). Rejecting 1000000 would turn that common operation into a hard decode failure for both SETATTR and CREATE. 1000000 * NSEC_PER_USEC is 10^9, which does not wrap on ILP32, so the Sun convention value passes through safely. Only genuinely out-of-range values (> 1000000) are rejected. The atime and mtime guards are therefore symmetric. The decoder only applied the Sun convention in the mtime block, which clears ATTR_ATIME_SET|ATTR_MTIME_SET when mtime useconds == 1000000. If a client puts 1000000 in the atime field but not in the mtime field, the atime block stored an out-of-range tv_nsec (10^9) and left ATTR_ATIME_SET set, so the bogus value reached the filesystem. Apply the convention in the atime block as well, clearing ATTR_ATIME_SET so the server uses its current time and ignores the value. Only ATTR_ATIME_SET is cleared there. The mtime block keeps its existing behavior, where 1000000 means "set both atime and mtime to now". Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Robbie Ko <robbieko@synology.com> [ cel: various tweaks, addenda, and clean-ups ] Link: https://patch.msgid.link/20260616054027.2360930-1-robbieko@synology.com Signed-off-by: Chuck Lever <cel@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Conflicts: fs/nfsd/nfsxdr.c [OLK-5.10 lacks the NFSv2 xdr_stream refactor series (2fdd6bd293b9 SETATTR, 7dcf65b91eca CREATE, 09f75a5375ac SYMLINK), so decode_sattr() is still the legacy pointer-based form; two of three hunks rejected. Adapted: tmp/tmp1 map to stable's tmp1/tmp2; return NULL instead of false (xdr_argsize_check(NULL) returns 0, yielding GARBAGE_ARGS); kept "* 1000" instead of NSEC_PER_USEC; NFS2_SATTR_SET_TO_SERVER_TIME added near file head] [Legacy nfssvc_decode_symlinkargs() discarded decode_sattr()'s return value; added "p =" assignment and NULL check returning 0, matching the upstream xdr_stream symlink decoder. SETATTR and CREATE propagate NULL via xdr_argsize_check().] Signed-off-by: Lu Jialin <lujialin4@huawei.com> --- fs/nfsd/nfsxdr.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c index 8a288c8fcd57..10fac01a6ba3 100644 --- a/fs/nfsd/nfsxdr.c +++ b/fs/nfsd/nfsxdr.c @@ -11,6 +11,16 @@ #define NFSDDBG_FACILITY NFSDDBG_XDR +/* + * Sun convention: a sattr time-useconds field of one full second (an + * otherwise out-of-range value) means "set this time to the current + * server time." It's needed to make permissions checks for the "touch" + * program across NFSv2 mounts work correctly. See description of + * sattr in section 6.1 of "NFS Illustrated" by Brent Callaghan, + * Addison-Wesley, ISBN 0-201-32750-5 + */ +#define NFS2_SATTR_SET_TO_SERVER_TIME (1000000) + /* * Mapping of S_IF* types to NFS file types */ @@ -101,25 +111,27 @@ decode_sattr(__be32 *p, struct iattr *iap, struct user_namespace *userns) } tmp = ntohl(*p++); tmp1 = ntohl(*p++); if (tmp != (u32)-1 && tmp1 != (u32)-1) { + /* + * Range test here to prevent the multiplication from + * wrapping to a valid (but incorrect) value on 32-bit + * platforms. + */ + if (tmp1 > NFS2_SATTR_SET_TO_SERVER_TIME) + return NULL; iap->ia_valid |= ATTR_ATIME | ATTR_ATIME_SET; iap->ia_atime.tv_sec = tmp; - iap->ia_atime.tv_nsec = tmp1 * 1000; + iap->ia_atime.tv_nsec = tmp1 * 1000; + if (tmp1 == NFS2_SATTR_SET_TO_SERVER_TIME) + iap->ia_valid &= ~ATTR_ATIME_SET; } tmp = ntohl(*p++); tmp1 = ntohl(*p++); if (tmp != (u32)-1 && tmp1 != (u32)-1) { + if (tmp1 > NFS2_SATTR_SET_TO_SERVER_TIME) + return NULL; iap->ia_valid |= ATTR_MTIME | ATTR_MTIME_SET; iap->ia_mtime.tv_sec = tmp; - iap->ia_mtime.tv_nsec = tmp1 * 1000; - /* - * Passing the invalid value useconds=1000000 for mtime - * is a Sun convention for "set both mtime and atime to - * current server time". It's needed to make permissions - * checks for the "touch" program across v2 mounts to - * Solaris and Irix boxes work correctly. See description of - * sattr in section 6.1 of "NFS Illustrated" by - * Brent Callaghan, Addison-Wesley, ISBN 0-201-32750-5 - */ - if (tmp1 == 1000000) + iap->ia_mtime.tv_nsec = tmp1 * 1000; + if (tmp1 == NFS2_SATTR_SET_TO_SERVER_TIME) iap->ia_valid &= ~(ATTR_ATIME_SET|ATTR_MTIME_SET); } return p; @@ -399,7 +411,9 @@ nfssvc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p) return 0; p += xdrlen; } - decode_sattr(p, &args->attrs, nfsd_user_namespace(rqstp)); + p = decode_sattr(p, &args->attrs, nfsd_user_namespace(rqstp)); + if (!p) + return 0; return 1; } -- 2.34.1
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://atomgit.com/openeuler/kernel/merge_requests/29885 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/RBK... FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://atomgit.com/openeuler/kernel/merge_requests/29885 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/RBK...
participants (2)
-
Lu Jialin -
patchwork bot