[PATCH OLK-6.6 0/2] CVE-2026-89713
Chuck Lever (1): NFSD: check truncate permission under inode lock NeilBrown (1): nfsd: Pass 'cred' instead of 'rqstp' to some functions. fs/nfsd/auth.c | 14 +++++++------- fs/nfsd/auth.h | 2 +- fs/nfsd/export.h | 3 ++- fs/nfsd/nfs4state.c | 3 ++- fs/nfsd/nfsfh.c | 6 +++--- fs/nfsd/nfsproc.c | 9 +++++---- fs/nfsd/vfs.c | 45 +++++++++++++++++++++++++++------------------ fs/nfsd/vfs.h | 4 ++-- 8 files changed, 49 insertions(+), 37 deletions(-) -- 2.52.0
From: NeilBrown <neilb@suse.de> mainline inclusion from mainline-v6.12-rc1 commit 9fd45c16f3e3b95b458e049c77f4d7dfef673a52 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/18761 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- nfsd_permission(), exp_rdonly(), nfsd_setuser(), and nfsexp_flags() only ever need the cred out of rqstp, so pass it explicitly instead of the whole rqstp. This makes the interfaces cleaner. Signed-off-by: NeilBrown <neilb@suse.de> Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Conflicts: fs/nfsd/export.h fs/nfsd/nfs4state.c fs/nfsd/nfsfh.c fs/nfsd/vfs.c fs/nfsd/vfs.h [Context conflicts] Signed-off-by: Yang Erkun <yangerkun@huawei.com> --- fs/nfsd/auth.c | 14 +++++++------- fs/nfsd/auth.h | 2 +- fs/nfsd/export.h | 3 ++- fs/nfsd/nfs4state.c | 3 ++- fs/nfsd/nfsfh.c | 6 +++--- fs/nfsd/nfsproc.c | 9 +++++---- fs/nfsd/vfs.c | 21 ++++++++++++--------- fs/nfsd/vfs.h | 4 ++-- 8 files changed, 34 insertions(+), 28 deletions(-) diff --git a/fs/nfsd/auth.c b/fs/nfsd/auth.c index e6beaaf4f170..93e33d1ee891 100644 --- a/fs/nfsd/auth.c +++ b/fs/nfsd/auth.c @@ -5,26 +5,26 @@ #include "nfsd.h" #include "auth.h" -int nfsexp_flags(struct svc_rqst *rqstp, struct svc_export *exp) +int nfsexp_flags(struct svc_cred *cred, struct svc_export *exp) { struct exp_flavor_info *f; struct exp_flavor_info *end = exp->ex_flavors + exp->ex_nflavors; for (f = exp->ex_flavors; f < end; f++) { - if (f->pseudoflavor == rqstp->rq_cred.cr_flavor) + if (f->pseudoflavor == cred->cr_flavor) return f->flags; } return exp->ex_flags; } -int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) +int nfsd_setuser(struct svc_cred *cred, struct svc_export *exp) { struct group_info *rqgi; struct group_info *gi; struct cred *new; int i; - int flags = nfsexp_flags(rqstp, exp); + int flags = nfsexp_flags(cred, exp); /* discard any old override before preparing the new set */ revert_creds(get_cred(current_real_cred())); @@ -32,10 +32,10 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) if (!new) return -ENOMEM; - new->fsuid = rqstp->rq_cred.cr_uid; - new->fsgid = rqstp->rq_cred.cr_gid; + new->fsuid = cred->cr_uid; + new->fsgid = cred->cr_gid; - rqgi = rqstp->rq_cred.cr_group_info; + rqgi = cred->cr_group_info; if (flags & NFSEXP_ALLSQUASH) { new->fsuid = exp->ex_anon_uid; diff --git a/fs/nfsd/auth.h b/fs/nfsd/auth.h index dbd66424f600..8c5031bbbcee 100644 --- a/fs/nfsd/auth.h +++ b/fs/nfsd/auth.h @@ -12,6 +12,6 @@ * Set the current process's fsuid/fsgid etc to those of the NFS * client user */ -int nfsd_setuser(struct svc_rqst *, struct svc_export *); +int nfsd_setuser(struct svc_cred *cred, struct svc_export *exp); #endif /* LINUX_NFSD_AUTH_H */ diff --git a/fs/nfsd/export.h b/fs/nfsd/export.h index 4a48b2ad5606..3174fc05bf02 100644 --- a/fs/nfsd/export.h +++ b/fs/nfsd/export.h @@ -99,7 +99,8 @@ struct svc_expkey { #define EX_NOHIDE(exp) ((exp)->ex_flags & NFSEXP_NOHIDE) #define EX_WGATHER(exp) ((exp)->ex_flags & NFSEXP_GATHERED_WRITES) -int nfsexp_flags(struct svc_rqst *rqstp, struct svc_export *exp); +struct svc_cred; +int nfsexp_flags(struct svc_cred *cred, struct svc_export *exp); __be32 check_xprtsec_policy(struct svc_export *exp, struct svc_rqst *rqstp); __be32 check_security_flavor(struct svc_export *exp, struct svc_rqst *rqstp); __be32 check_nfsd_access(struct svc_export *exp, struct svc_rqst *rqstp); diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 56f5c6b867ac..25dc60cb0515 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -6648,7 +6648,8 @@ nfs4_check_file(struct svc_rqst *rqstp, struct svc_fh *fhp, struct nfs4_stid *s, nf = nfs4_find_file(s, flags); if (nf) { - status = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry, + status = nfsd_permission(&rqstp->rq_cred, + fhp->fh_export, fhp->fh_dentry, acc | NFSD_MAY_OWNER_OVERRIDE); if (status) { nfsd_file_put(nf); diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 283c1a60c846..e41c1efc4699 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -102,7 +102,7 @@ static bool nfsd_originating_port_ok(struct svc_rqst *rqstp, int flags) static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp, struct svc_export *exp) { - int flags = nfsexp_flags(rqstp, exp); + int flags = nfsexp_flags(&rqstp->rq_cred, exp); /* Check if the request originated from a secure port. */ if (!nfsd_originating_port_ok(rqstp, flags)) { @@ -113,7 +113,7 @@ static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp, } /* Set user creds for this exportpoint */ - return nfserrno(nfsd_setuser(rqstp, exp)); + return nfserrno(nfsd_setuser(&rqstp->rq_cred, exp)); } static inline __be32 check_pseudo_root(struct svc_rqst *rqstp, @@ -402,7 +402,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access) skip_pseudoflavor_check: /* Finally, check access permissions. */ - error = nfsd_permission(rqstp, exp, dentry, access); + error = nfsd_permission(&rqstp->rq_cred, exp, dentry, access); out: trace_nfsd_fh_verify_err(rqstp, fhp, type, access, error); if (error == nfserr_stale) diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index a7315928a760..0a7e7728e43e 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -331,10 +331,11 @@ nfsd_proc_create(struct svc_rqst *rqstp) * echo thing > device-special-file-or-pipe * by doing a CREATE with type==0 */ - resp->status = nfsd_permission(rqstp, - newfhp->fh_export, - newfhp->fh_dentry, - NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS); + resp->status = nfsd_permission( + &rqstp->rq_cred, + newfhp->fh_export, + newfhp->fh_dentry, + NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS); if (resp->status && resp->status != nfserr_rofs) goto out_unlock; } diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 2ded9798ad28..5ad51462e0d2 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -423,8 +423,9 @@ nfsd_get_write_access(struct svc_rqst *rqstp, struct svc_fh *fhp, if (iap->ia_size < inode->i_size) { __be32 err; - err = nfsd_permission(rqstp, fhp->fh_export, fhp->fh_dentry, - NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE); + err = nfsd_permission(&rqstp->rq_cred, + fhp->fh_export, fhp->fh_dentry, + NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE); if (err) return err; } @@ -803,7 +804,8 @@ nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *suppor sresult |= map->access; - err2 = nfsd_permission(rqstp, export, dentry, map->how); + err2 = nfsd_permission(&rqstp->rq_cred, export, + dentry, map->how); switch (err2) { case nfs_ok: result |= map->access; @@ -1448,7 +1450,8 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp, dirp = d_inode(dentry); dchild = dget(resfhp->fh_dentry); - err = nfsd_permission(rqstp, fhp->fh_export, dentry, NFSD_MAY_CREATE); + err = nfsd_permission(&rqstp->rq_cred, fhp->fh_export, dentry, + NFSD_MAY_CREATE); if (err) goto out; @@ -2175,9 +2178,9 @@ nfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct kstatfs *stat, in return err; } -static int exp_rdonly(struct svc_rqst *rqstp, struct svc_export *exp) +static int exp_rdonly(struct svc_cred *cred, struct svc_export *exp) { - return nfsexp_flags(rqstp, exp) & NFSEXP_READONLY; + return nfsexp_flags(cred, exp) & NFSEXP_READONLY; } #ifdef CONFIG_NFSD_V4 @@ -2421,8 +2424,8 @@ nfsd_setxattr(struct svc_rqst *rqstp, struct svc_fh *fhp, char *name, * Check for a user's access permissions to this inode. */ __be32 -nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, - struct dentry *dentry, int acc) +nfsd_permission(struct svc_cred *cred, struct svc_export *exp, + struct dentry *dentry, int acc) { struct inode *inode = d_inode(dentry); int err; @@ -2453,7 +2456,7 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp, */ if (!(acc & NFSD_MAY_LOCAL_ACCESS)) if (acc & (NFSD_MAY_WRITE | NFSD_MAY_SATTR | NFSD_MAY_TRUNC)) { - if (exp_rdonly(rqstp, exp) || + if (exp_rdonly(cred, exp) || __mnt_is_readonly(exp->ex_path.mnt)) return nfserr_rofs; if (/* (acc & NFSD_MAY_WRITE) && */ IS_IMMUTABLE(inode)) diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h index 49a1389532d0..e4e7eedbc989 100644 --- a/fs/nfsd/vfs.h +++ b/fs/nfsd/vfs.h @@ -153,8 +153,8 @@ __be32 nfsd_readdir(struct svc_rqst *, struct svc_fh *, __be32 nfsd_statfs(struct svc_rqst *, struct svc_fh *, struct kstatfs *, int access); -__be32 nfsd_permission(struct svc_rqst *, struct svc_export *, - struct dentry *, int); +__be32 nfsd_permission(struct svc_cred *cred, struct svc_export *exp, + struct dentry *dentry, int acc); static inline int fh_want_write(struct svc_fh *fh) { -- 2.52.0
From: Chuck Lever <chuck.lever@oracle.com> mainline inclusion from mainline-v7.3-rc1 commit b778e0e0a16759f22a70579c3cf8d254a40d4a7f category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/18761 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- nfsd_setattr() checks whether a size update needs NFSD_MAY_TRUNC before it takes inode_lock(). The comparison uses the file size sampled by that unlocked read, but the actual ATTR_SIZE update is applied later under inode_lock() by notify_change(). This leaves a TOCTOU window for append-only files. If a client sends a SETATTR that does not shrink the file at the time of the unlocked sample, a concurrent append can extend the file before nfsd_setattr() takes inode_lock(). notify_change() then applies a real truncation without the NFSD_MAY_TRUNC check that rejects IS_APPEND(inode). The VFS truncate syscall paths perform their own append-only checks before calling notify_change(), so NFSD must make this decision against the locked size it is about to change. Split the write-count acquisition from the truncation permission check. Keep get_write_access() before the locked setattr work, then recheck whether the requested size is below i_size_read(inode) after inode_lock() has been acquired and before notify_change(ATTR_SIZE). This also avoids the plain unlocked inode->i_size load. Fixes: 783112f7401f ("nfsd: special case truncates some more") Cc: stable@vger.kernel.org Assisted-by: kres:claude-opus-4-7 Reported-by: Chris Mason <clm@meta.com> Signed-off-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260530-nfsd-fixes-v2-6-f27e8eb4d974@kernel.org Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Conflicts: fs/nfsd/vfs.c [Context conflicts] Signed-off-by: Yang Erkun <yangerkun@huawei.com> --- fs/nfsd/vfs.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 5ad51462e0d2..710ab8be6b86 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -415,21 +415,22 @@ nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap) } static __be32 -nfsd_get_write_access(struct svc_rqst *rqstp, struct svc_fh *fhp, - struct iattr *iap) +nfsd_may_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, + struct iattr *iap) { struct inode *inode = d_inode(fhp->fh_dentry); - if (iap->ia_size < inode->i_size) { - __be32 err; + if (iap->ia_size >= i_size_read(inode)) + return nfs_ok; - err = nfsd_permission(&rqstp->rq_cred, - fhp->fh_export, fhp->fh_dentry, - NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE); - if (err) - return err; - } - return nfserrno(get_write_access(inode)); + return nfsd_permission(&rqstp->rq_cred, fhp->fh_export, fhp->fh_dentry, + NFSD_MAY_TRUNC | NFSD_MAY_OWNER_OVERRIDE); +} + +static __be32 +nfsd_get_write_access(struct svc_fh *fhp) +{ + return nfserrno(get_write_access(d_inode(fhp->fh_dentry))); } static int __nfsd_setattr(struct dentry *dentry, struct iattr *iap) @@ -551,12 +552,17 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, * setattr call. */ if (size_change) { - err = nfsd_get_write_access(rqstp, fhp, iap); + err = nfsd_get_write_access(fhp); if (err) return err; } inode_lock(inode); + if (size_change) { + err = nfsd_may_truncate(rqstp, fhp, iap); + if (err) + goto out_unlock; + } err = fh_fill_pre_attrs(fhp); if (err) goto out_unlock; -- 2.52.0
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://atomgit.com/openeuler/kernel/merge_requests/28615 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/K5L... 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/28615 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/K5L...
participants (2)
-
patchwork bot -
Yang Erkun