ovl_open_realfile reletion code feature patch ("ovl: pass layer mnt to ovl_open_realfile: ovl_open_realfile reletion.") init_file code refactoring patch ("fs: move kmem_cache_zalloc() into alloc_empty_file*() helpers") backing_file structure and backing_file_open function reletion features: patch ("fs: use backing_file container for internal files with "fake" f_path") patch ("fs: move cleanup from init_file() into its callers") bpf_fd_pass const warning fix patch ("lsm: constify the 'file' parameter in security_binder_transfer_file()") code refactoring: store real path instead of fake path in backing file f_path patch ("fs: get mnt_writers count for an open backing file's real path") patch ("fs: create helper file_user_path() for user displayed mapped file path") patch ("fs: store real path instead of fake path in backing file f_path") patch ("ovl: Fix nested backing file paths") patch ("perf/core: Fix MMAP event path names with backing files") backing_file const warning fix patch ("fs: constify file ptr in backing_file accessor helpers") SELinux overlay backing_file mmap/mprotect check prepare for lsm function: patch ("fs: prepare for adding LSM blob to backing_file") patch ("lsm: add backing_file LSM hooks") CVE-2026-46054 fix: Add SELinux lsm hooks for backing_file mmap/mprotect patch ("selinux: fix overlayfs mmap() and mprotect() access checks") Adrian Hunter (1): perf/core: Fix MMAP event path names with backing files Amir Goldstein (9): [Backport] ovl: pass layer mnt to ovl_open_realfile() fs: move kmem_cache_zalloc() into alloc_empty_file*() helpers fs: use backing_file container for internal files with "fake" f_path fs: move cleanup from init_file() into its callers fs: get mnt_writers count for an open backing file's real path fs: create helper file_user_path() for user displayed mapped file path fs: store real path instead of fake path in backing file f_path fs: constify file ptr in backing_file accessor helpers fs: prepare for adding LSM blob to backing_file André Almeida (1): ovl: Fix nested backing file paths Khadija Kamran (1): lsm: constify the 'file' parameter in security_binder_transfer_file() Paul Moore (2): lsm: add backing_file LSM hooks selinux: fix overlayfs mmap() and mprotect() access checks arch/arc/kernel/troubleshoot.c | 6 +- fs/file_table.c | 139 ++++++++++++++--- fs/internal.h | 18 ++- fs/open.c | 76 ++++++--- fs/overlayfs/file.c | 34 ++-- fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/super.c | 16 +- fs/overlayfs/util.c | 14 ++ fs/proc/base.c | 2 +- fs/proc/nommu.c | 2 +- fs/proc/task_mmu.c | 4 +- fs/proc/task_nommu.c | 2 +- include/linux/fs.h | 44 +++++- include/linux/lsm_audit.h | 2 +- include/linux/lsm_hook_defs.h | 7 +- include/linux/lsm_hooks.h | 1 + include/linux/security.h | 26 +++- kernel/events/core.c | 2 +- kernel/trace/trace_output.c | 2 +- security/security.c | 112 ++++++++++++- security/selinux/hooks.c | 250 ++++++++++++++++++++++-------- security/selinux/include/objsec.h | 11 ++ 22 files changed, 631 insertions(+), 140 deletions(-) -- 2.18.0.huawei.25
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://atomgit.com/openeuler/kernel/merge_requests/24754 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/AIP... 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/24754 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/AIP...
From: Amir Goldstein <amir73il@gmail.com> mainline inclusion from mainline-v5.19-rc1 commit 1248ea4b91bcdafefdb025087e67d58382cfc9eb category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15316 CVE: CVE-2026-46054 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- Ensure that ovl_open_realfile() takes the mount's idmapping into account. We add a new helper ovl_path_realdata() that can be used to easily retrieve the relevant path which we can pass down. This is needed to support idmapped base layers with overlay. Cc: <linux-unionfs@vger.kernel.org> Tested-by: Giuseppe Scrivano <gscrivan@redhat.com> Reviewed-by: Christian Brauner (Microsoft) <brauner@kernel.org> Signed-off-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Christian Brauner (Microsoft) <brauner@kernel.org> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> Conflicts: fs/overlayfs/util.c [Only context conflicts.] Signed-off-by: Cai Xinchen <caixinchen1@huawei.com> --- fs/overlayfs/file.c | 22 +++++++++++++--------- fs/overlayfs/overlayfs.h | 1 + fs/overlayfs/util.c | 14 ++++++++++++++ 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 4440ff43cb66..343db8b3ecd6 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -37,8 +37,9 @@ static char ovl_whatisit(struct inode *inode, struct inode *realinode) #define OVL_OPEN_FLAGS (O_NOATIME | FMODE_NONOTIFY) static struct file *ovl_open_realfile(const struct file *file, - struct inode *realinode) + struct path *realpath) { + struct inode *realinode = d_inode(realpath->dentry); struct inode *inode = file_inode(file); struct file *realfile; const struct cred *old_cred; @@ -103,21 +104,21 @@ static int ovl_change_flags(struct file *file, unsigned int flags) static int ovl_real_fdget_meta(const struct file *file, struct fd *real, bool allow_meta) { - struct inode *inode = file_inode(file); - struct inode *realinode; + struct dentry *dentry = file_dentry(file); + struct path realpath; real->flags = 0; real->file = file->private_data; if (allow_meta) - realinode = ovl_inode_real(inode); + ovl_path_real(dentry, &realpath); else - realinode = ovl_inode_realdata(inode); + ovl_path_realdata(dentry, &realpath); /* Has it been copied up since we'd opened it? */ - if (unlikely(file_inode(real->file) != realinode)) { + if (unlikely(file_inode(real->file) != d_inode(realpath.dentry))) { real->flags = FDPUT_FPUT; - real->file = ovl_open_realfile(file, realinode); + real->file = ovl_open_realfile(file, &realpath); return PTR_ERR_OR_ZERO(real->file); } @@ -143,17 +144,20 @@ static int ovl_real_fdget(const struct file *file, struct fd *real) static int ovl_open(struct inode *inode, struct file *file) { + struct dentry *dentry = file_dentry(file); struct file *realfile; + struct path realpath; int err; - err = ovl_maybe_copy_up(file_dentry(file), file->f_flags); + err = ovl_maybe_copy_up(dentry, file->f_flags); if (err) return err; /* No longer need these flags, so don't pass them on to underlying fs */ file->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); - realfile = ovl_open_realfile(file, ovl_inode_realdata(inode)); + ovl_path_realdata(dentry, &realpath); + realfile = ovl_open_realfile(file, &realpath); if (IS_ERR(realfile)) return PTR_ERR(realfile); diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 7fc00ebd2230..92ce85eb8d4c 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -267,6 +267,7 @@ void ovl_path_upper(struct dentry *dentry, struct path *path); void ovl_path_lower(struct dentry *dentry, struct path *path); void ovl_path_lowerdata(struct dentry *dentry, struct path *path); enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path); +enum ovl_path_type ovl_path_realdata(struct dentry *dentry, struct path *path); struct dentry *ovl_dentry_upper(struct dentry *dentry); struct dentry *ovl_dentry_lower(struct dentry *dentry); struct dentry *ovl_dentry_lowerdata(struct dentry *dentry); diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 62a258c2b59c..5763b64a2d6a 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -209,6 +209,20 @@ enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path) return type; } +enum ovl_path_type ovl_path_realdata(struct dentry *dentry, struct path *path) +{ + enum ovl_path_type type = ovl_path_type(dentry); + + WARN_ON_ONCE(d_is_dir(dentry)); + + if (!OVL_TYPE_UPPER(type) || OVL_TYPE_MERGE(type)) + ovl_path_lowerdata(dentry, path); + else + ovl_path_upper(dentry, path); + + return type; +} + struct dentry *ovl_dentry_upper(struct dentry *dentry) { return ovl_upperdentry_dereference(OVL_I(d_inode(dentry))); -- 2.18.0.huawei.25
From: Amir Goldstein <amir73il@gmail.com> mainline inclusion from mainline-v6.5-rc1 commit 8a05a8c31d06c5d0d67b273a4a00f87269adde82 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15316 CVE: CVE-2026-46054 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- Use a common helper init_file() instead of __alloc_file() for alloc_empty_file*() helpers and improrve the documentation. This is needed for a follow up patch that allocates a backing_file container. Suggested-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Amir Goldstein <amir73il@gmail.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Message-Id: <20230615112229.2143178-4-amir73il@gmail.com> Signed-off-by: Christian Brauner <brauner@kernel.org> Conflicts: fs/file_table.c [OLK-5.10 use kmem_cache_zalloc_file instead of kmem_cache_zalloc] Signed-off-by: Cai Xinchen <caixinchen1@huawei.com> --- fs/file_table.c | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/fs/file_table.c b/fs/file_table.c index 935fdd668e05..445b60393f0c 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -124,20 +124,15 @@ int proc_nr_files(struct ctl_table *table, int write, } #endif -static struct file *__alloc_file(int flags, const struct cred *cred) +static int init_file(struct file *f, int flags, const struct cred *cred) { - struct file *f; int error; - f = kmem_cache_zalloc_file(); - if (unlikely(!f)) - return ERR_PTR(-ENOMEM); - f->f_cred = get_cred(cred); error = security_file_alloc(f); if (unlikely(error)) { file_free_rcu(&f->f_u.fu_rcuhead); - return ERR_PTR(error); + return error; } atomic_long_set(&f->f_count, 1); @@ -149,7 +144,7 @@ static struct file *__alloc_file(int flags, const struct cred *cred) f->f_mode = OPEN_FMODE(flags); /* f->f_version: 0 */ - return f; + return 0; } /* Find an unused file structure and return a pointer to it. @@ -166,6 +161,7 @@ struct file *alloc_empty_file(int flags, const struct cred *cred) { static long old_max; struct file *f; + int error; /* * Privileged users can go above max_files @@ -179,9 +175,15 @@ struct file *alloc_empty_file(int flags, const struct cred *cred) goto over; } - f = __alloc_file(flags, cred); - if (!IS_ERR(f)) - percpu_counter_inc(&nr_files); + f = kmem_cache_zalloc_file(); + if (unlikely(!f)) + return ERR_PTR(-ENOMEM); + + error = init_file(f, flags, cred); + if (unlikely(error)) + return ERR_PTR(error); + + percpu_counter_inc(&nr_files); return f; @@ -197,14 +199,23 @@ struct file *alloc_empty_file(int flags, const struct cred *cred) /* * Variant of alloc_empty_file() that doesn't check and modify nr_files. * - * Should not be used unless there's a very good reason to do so. + * This is only for kernel internal use, and the allocate file must not be + * installed into file tables or such. */ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred) { - struct file *f = __alloc_file(flags, cred); + struct file *f; + int error; + + f = kmem_cache_zalloc_file(); + if (unlikely(!f)) + return ERR_PTR(-ENOMEM); + + error = init_file(f, flags, cred); + if (unlikely(error)) + return ERR_PTR(error); - if (!IS_ERR(f)) - f->f_mode |= FMODE_NOACCOUNT; + f->f_mode |= FMODE_NOACCOUNT; return f; } -- 2.18.0.huawei.25
From: Amir Goldstein <amir73il@gmail.com> mainline inclusion from mainline-v6.5-rc1 commit 62d53c4a1dfe347bd87ede46ffad38c9a3870338 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15316 CVE: CVE-2026-46054 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- Overlayfs uses open_with_fake_path() to allocate internal kernel files, with a "fake" path - whose f_path is not on the same fs as f_inode. Allocate a container struct backing_file for those internal files, that is used to hold the "fake" ovl path along with the real path. backing_file_real_path() can be used to access the stored real path. Signed-off-by: Amir Goldstein <amir73il@gmail.com> Message-Id: <20230615112229.2143178-5-amir73il@gmail.com> Signed-off-by: Christian Brauner <brauner@kernel.org> Conflicts: fs/file_table.c fs/internal.h fs/open.c fs/overlayfs/file.c include/linux/fs.h [fs/file_table.c: Add GET_FILE_WRAP to kmem_cache_free to adapt OLK-5.10 Others only context conflicts.] Signed-off-by: Cai Xinchen <caixinchen1@huawei.com> --- fs/file_table.c | 50 +++++++++++++++++++++++++++++++++++++++++++-- fs/internal.h | 5 +++-- fs/open.c | 45 +++++++++++++++++++++++++++++----------- fs/overlayfs/file.c | 4 ++-- include/linux/fs.h | 31 +++++++++++++++++++++++++--- 5 files changed, 114 insertions(+), 21 deletions(-) diff --git a/fs/file_table.c b/fs/file_table.c index 445b60393f0c..94e99c9700cd 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -73,18 +73,40 @@ static inline struct file *kmem_cache_zalloc_file(void) } #endif +/* Container for backing file with optional real path */ +struct backing_file { + struct file file; + struct path real_path; +}; + +static inline struct backing_file *backing_file(struct file *f) +{ + return container_of(f, struct backing_file, file); +} + +struct path *backing_file_real_path(struct file *f) +{ + return &backing_file(f)->real_path; +} +EXPORT_SYMBOL_GPL(backing_file_real_path); + static void file_free_rcu(struct rcu_head *head) { struct file *f = container_of(head, struct file, f_u.fu_rcuhead); put_cred(f->f_cred); - kmem_cache_free(filp_cachep, GET_FILE_WRAP(f)); + if (unlikely(f->f_mode & FMODE_BACKING)) + kfree(backing_file(f)); + else + kmem_cache_free(filp_cachep, GET_FILE_WRAP(f)); } static inline void file_free(struct file *f) { security_file_free(f); - if (!(f->f_mode & FMODE_NOACCOUNT)) + if (unlikely(f->f_mode & FMODE_BACKING)) + path_put(backing_file_real_path(f)); + if (likely(!(f->f_mode & FMODE_NOACCOUNT))) percpu_counter_dec(&nr_files); call_rcu(&f->f_u.fu_rcuhead, file_free_rcu); } @@ -220,6 +242,30 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred) return f; } +/* + * Variant of alloc_empty_file() that allocates a backing_file container + * and doesn't check and modify nr_files. + * + * This is only for kernel internal use, and the allocate file must not be + * installed into file tables or such. + */ +struct file *alloc_empty_backing_file(int flags, const struct cred *cred) +{ + struct backing_file *ff; + int error; + + ff = kzalloc(sizeof(struct backing_file), GFP_KERNEL); + if (unlikely(!ff)) + return ERR_PTR(-ENOMEM); + + error = init_file(&ff->file, flags, cred); + if (unlikely(error)) + return ERR_PTR(error); + + ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT; + return &ff->file; +} + /** * alloc_file - allocate and initialize a 'struct file' * diff --git a/fs/internal.h b/fs/internal.h index 0a9e7e9f24a6..1ba9a0a3e4a5 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -107,8 +107,9 @@ extern void chroot_fs_refs(const struct path *, const struct path *); /* * file_table.c */ -extern struct file *alloc_empty_file(int, const struct cred *); -extern struct file *alloc_empty_file_noaccount(int, const struct cred *); +struct file *alloc_empty_file(int flags, const struct cred *cred); +struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred); +struct file *alloc_empty_backing_file(int flags, const struct cred *cred); static inline void put_file_access(struct file *file) { diff --git a/fs/open.c b/fs/open.c index 96de0d3f1a8b..585ab626f802 100644 --- a/fs/open.c +++ b/fs/open.c @@ -951,23 +951,44 @@ struct file *dentry_open(const struct path *path, int flags, } EXPORT_SYMBOL(dentry_open); -struct file *open_with_fake_path(const struct path *path, int flags, - struct inode *inode, const struct cred *cred) +/** + * backing_file_open - open a backing file for kernel internal use + * @path: path of the file to open + * @flags: open flags + * @path: path of the backing file + * @cred: credentials for open + * + * Open a backing file for a stackable filesystem (e.g., overlayfs). + * @path may be on the stackable filesystem and backing inode on the + * underlying filesystem. In this case, we want to be able to return + * the @real_path of the backing inode. This is done by embedding the + * returned file into a container structure that also stores the path of + * the backing inode on the underlying filesystem, which can be + * retrieved using backing_file_real_path(). + */ +struct file *backing_file_open(const struct path *path, int flags, + const struct path *real_path, + const struct cred *cred) { - struct file *f = alloc_empty_file_noaccount(flags, cred); - if (!IS_ERR(f)) { - int error; + struct file *f; + int error; - f->f_path = *path; - error = do_dentry_open(f, inode, NULL); - if (error) { - fput(f); - f = ERR_PTR(error); - } + f = alloc_empty_backing_file(flags, cred); + if (IS_ERR(f)) + return f; + + f->f_path = *path; + path_get(real_path); + *backing_file_real_path(f) = *real_path; + error = do_dentry_open(f, d_inode(real_path->dentry), NULL); + if (error) { + fput(f); + f = ERR_PTR(error); } + return f; } -EXPORT_SYMBOL(open_with_fake_path); +EXPORT_SYMBOL_GPL(backing_file_open); #define WILL_CREATE(flags) (flags & (O_CREAT | __O_TMPFILE)) #define O_PATH_FLAGS (O_DIRECTORY | O_NOFOLLOW | O_PATH | O_CLOEXEC) diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 343db8b3ecd6..460748b63806 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -58,8 +58,8 @@ static struct file *ovl_open_realfile(const struct file *file, if (!inode_owner_or_capable(realinode)) flags &= ~O_NOATIME; - realfile = open_with_fake_path(&file->f_path, flags, realinode, - current_cred()); + realfile = backing_file_open(&file->f_path, flags, realpath, + current_cred()); } revert_creds(old_cred); diff --git a/include/linux/fs.h b/include/linux/fs.h index 221b4d4e3889..9cea290c15fb 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -169,6 +169,9 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset, /* File is stream-like */ #define FMODE_STREAM ((__force fmode_t)0x200000) +/* File is embedded in backing_file object */ +#define FMODE_BACKING ((__force fmode_t)0x2000000) + /* File was opened by fanotify and shouldn't generate fanotify events */ #define FMODE_NONOTIFY ((__force fmode_t)0x4000000) @@ -2713,9 +2716,31 @@ static inline struct file *file_open_root_mnt(struct vfsmount *mnt, return file_open_root(&(struct path){.mnt = mnt, .dentry = mnt->mnt_root}, name, flags, mode); } -extern struct file * dentry_open(const struct path *, int, const struct cred *); -extern struct file * open_with_fake_path(const struct path *, int, - struct inode*, const struct cred *); +struct file *dentry_open(const struct path *path, int flags, + const struct cred *creds); +struct file *dentry_create(const struct path *path, int flags, umode_t mode, + const struct cred *cred); +struct file *backing_file_open(const struct path *path, int flags, + const struct path *real_path, + const struct cred *cred); +struct path *backing_file_real_path(struct file *f); + +/* + * file_real_path - get the path corresponding to f_inode + * + * When opening a backing file for a stackable filesystem (e.g., + * overlayfs) f_path may be on the stackable filesystem and f_inode on + * the underlying filesystem. When the path associated with f_inode is + * needed, this helper should be used instead of accessing f_path + * directly. +*/ +static inline const struct path *file_real_path(struct file *f) +{ + if (unlikely(f->f_mode & FMODE_BACKING)) + return backing_file_real_path(f); + return &f->f_path; +} + static inline struct file *file_clone_open(struct file *file) { return dentry_open(&file->f_path, file->f_flags, file->f_cred); -- 2.18.0.huawei.25
From: Amir Goldstein <amir73il@gmail.com> mainline inclusion from mainline-v6.5-rc1 commit dff745c1221a402b4921d54f292288373cff500c category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15316 CVE: CVE-2026-46054 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- The use of file_free_rcu() in init_file() to free the struct that was allocated by the caller was hacky and we got what we deserved. Let init_file() and its callers take care of cleaning up each after their own allocated resources on error. Fixes: 62d53c4a1dfe ("fs: use backing_file container for internal files with "fake" f_path") # mainline only Reported-and-tested-by: syzbot+ada42aab05cf51b00e98@syzkaller.appspotmail.com Signed-off-by: Amir Goldstein <amir73il@gmail.com> Message-Id: <20230701171134.239409-1-amir73il@gmail.com> Signed-off-by: Christian Brauner <brauner@kernel.org> Conflicts: fs/file_table.c [Original patch is file_free_rcu(&f->f_rcuhead); But for 5.10 is file_free_rcu(&f_u.fu_rcuhead). We replace that with put_cred(f->f_cred).] Signed-off-by: Cai Xinchen <caixinchen1@huawei.com> --- fs/file_table.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/fs/file_table.c b/fs/file_table.c index 94e99c9700cd..3d27e490d163 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -153,7 +153,7 @@ static int init_file(struct file *f, int flags, const struct cred *cred) f->f_cred = get_cred(cred); error = security_file_alloc(f); if (unlikely(error)) { - file_free_rcu(&f->f_u.fu_rcuhead); + put_cred(f->f_cred); return error; } @@ -202,8 +202,10 @@ struct file *alloc_empty_file(int flags, const struct cred *cred) return ERR_PTR(-ENOMEM); error = init_file(f, flags, cred); - if (unlikely(error)) + if (unlikely(error)) { + kmem_cache_free(filp_cachep, f); return ERR_PTR(error); + } percpu_counter_inc(&nr_files); @@ -234,8 +236,10 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred) return ERR_PTR(-ENOMEM); error = init_file(f, flags, cred); - if (unlikely(error)) + if (unlikely(error)) { + kmem_cache_free(filp_cachep, f); return ERR_PTR(error); + } f->f_mode |= FMODE_NOACCOUNT; @@ -259,8 +263,10 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred) return ERR_PTR(-ENOMEM); error = init_file(&ff->file, flags, cred); - if (unlikely(error)) + if (unlikely(error)) { + kfree(ff); return ERR_PTR(error); + } ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT; return &ff->file; -- 2.18.0.huawei.25
From: Khadija Kamran <kamrankhadijadj@gmail.com> mainline inclusion from mainline-v6.6-rc1 commit 8e4672d6f902d5c4db1e87e8aa9f530149d85bc6 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15316 CVE: CVE-2026-46054 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- SELinux registers the implementation for the "binder_transfer_file" hook. Looking at the function implementation we observe that the parameter "file" is not changing. Mark the "file" parameter of LSM hook security_binder_transfer_file() as "const" since it will not be changing in the LSM hook. Signed-off-by: Khadija Kamran <kamrankhadijadj@gmail.com> [PM: subject line whitespace fix] Signed-off-by: Paul Moore <paul@paul-moore.com> Conflicts: security/security.c include/linux/lsm_hook_defs.h include/linux/security.h [Only context conflicts.] Signed-off-by: Cai Xinchen <caixinchen1@huawei.com> --- include/linux/lsm_hook_defs.h | 2 +- include/linux/security.h | 4 ++-- security/security.c | 2 +- security/selinux/hooks.c | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index 350b6d9ea39e..eb15ba8b2c7d 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -32,7 +32,7 @@ LSM_HOOK(int, 0, binder_transaction, const struct cred *from, LSM_HOOK(int, 0, binder_transfer_binder, const struct cred *from, const struct cred *to) LSM_HOOK(int, 0, binder_transfer_file, const struct cred *from, - const struct cred *to, struct file *file) + const struct cred *to, const struct file *file) LSM_HOOK(int, 0, ptrace_access_check, struct task_struct *child, unsigned int mode) LSM_HOOK(int, 0, ptrace_traceme, struct task_struct *parent) diff --git a/include/linux/security.h b/include/linux/security.h index 0d33b61d6f82..b9cd823992f5 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -264,7 +264,7 @@ int security_binder_transaction(const struct cred *from, int security_binder_transfer_binder(const struct cred *from, const struct cred *to); int security_binder_transfer_file(const struct cred *from, - const struct cred *to, struct file *file); + const struct cred *to, const struct file *file); int security_ptrace_access_check(struct task_struct *child, unsigned int mode); int security_ptrace_traceme(struct task_struct *parent); int security_capget(struct task_struct *target, @@ -519,7 +519,7 @@ static inline int security_binder_transfer_binder(const struct cred *from, static inline int security_binder_transfer_file(const struct cred *from, const struct cred *to, - struct file *file) + const struct file *file) { return 0; } diff --git a/security/security.c b/security/security.c index 98b8ce46d113..a86aa7f34ce2 100644 --- a/security/security.c +++ b/security/security.c @@ -767,7 +767,7 @@ int security_binder_transfer_binder(const struct cred *from, } int security_binder_transfer_file(const struct cred *from, - const struct cred *to, struct file *file) + const struct cred *to, const struct file *file) { return call_int_hook(binder_transfer_file, 0, from, to, file); } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 3559303272e8..0973cc29227f 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1694,7 +1694,7 @@ static inline int file_path_has_perm(const struct cred *cred, } #ifdef CONFIG_BPF_SYSCALL -static int bpf_fd_pass(struct file *file, u32 sid); +static int bpf_fd_pass(const struct file *file, u32 sid); #endif /* Check whether a task can use an open file descriptor to @@ -1967,7 +1967,7 @@ static inline u32 file_mask_to_av(int mode, int mask) } /* Convert a Linux file to an access vector. */ -static inline u32 file_to_av(struct file *file) +static inline u32 file_to_av(const struct file *file) { u32 av = 0; @@ -2045,7 +2045,7 @@ static int selinux_binder_transfer_binder(const struct cred *from, static int selinux_binder_transfer_file(const struct cred *from, const struct cred *to, - struct file *file) + const struct file *file) { u32 sid = cred_sid(to); struct file_security_struct *fsec = selinux_file(file); @@ -6784,7 +6784,7 @@ static u32 bpf_map_fmode_to_av(fmode_t fmode) * access the bpf object and that's why we have to add this additional check in * selinux_file_receive and selinux_binder_transfer_files. */ -static int bpf_fd_pass(struct file *file, u32 sid) +static int bpf_fd_pass(const struct file *file, u32 sid) { struct bpf_security_struct *bpfsec; struct bpf_prog *prog; -- 2.18.0.huawei.25
From: Amir Goldstein <amir73il@gmail.com> mainline inclusion from mainline-v6.7-rc1 commit 83bc1d294130cc471a89ce10770daa281a93fcb0 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15316 CVE: CVE-2026-46054 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- A writeable mapped backing file can perform writes to the real inode. Therefore, the real path mount must be kept writable so long as the writable map exists. This may not be strictly needed for ovelrayfs private upper mount, but it is correct to take the mnt_writers count in the vfs helper. Signed-off-by: Amir Goldstein <amir73il@gmail.com> Link: https://lore.kernel.org/r/20231009153712.1566422-2-amir73il@gmail.com Signed-off-by: Christian Brauner <brauner@kernel.org> Conflicts: fs/internal.h fs/open.c [fs/internal.h: 5.10 use __mnt_drop_write instead of mnt_put_write_access. fs/open.c: 5.10 use __mnt_want_write instead of mnt_get_write_access.] Signed-off-by: Cai Xinchen <caixinchen1@huawei.com> --- fs/internal.h | 11 +++++++++-- fs/open.c | 31 +++++++++++++++++++++++++------ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/fs/internal.h b/fs/internal.h index 1ba9a0a3e4a5..c143ffd1a174 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -111,13 +111,20 @@ struct file *alloc_empty_file(int flags, const struct cred *cred); struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred); struct file *alloc_empty_backing_file(int flags, const struct cred *cred); +static inline void file_put_write_access(struct file *file) +{ + put_write_access(file->f_inode); + __mnt_drop_write(file->f_path.mnt); + if (unlikely(file->f_mode & FMODE_BACKING)) + __mnt_drop_write(backing_file_real_path(file)->mnt); +} + static inline void put_file_access(struct file *file) { if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) { i_readcount_dec(file->f_inode); } else if (file->f_mode & FMODE_WRITER) { - put_write_access(file->f_inode); - __mnt_drop_write(file->f_path.mnt); + file_put_write_access(file); } } diff --git a/fs/open.c b/fs/open.c index 585ab626f802..16a11f8d6116 100644 --- a/fs/open.c +++ b/fs/open.c @@ -760,6 +760,30 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd, uid_t, user, gid_t, group) return ksys_fchown(fd, user, group); } +static inline int file_get_write_access(struct file *f) +{ + int error; + + error = get_write_access(f->f_inode); + if (unlikely(error)) + return error; + error = __mnt_want_write(f->f_path.mnt); + if (unlikely(error)) + goto cleanup_inode; + if (unlikely(f->f_mode & FMODE_BACKING)) { + error = __mnt_want_write(backing_file_real_path(f)->mnt); + if (unlikely(error)) + goto cleanup_mnt; + } + return 0; + +cleanup_mnt: + __mnt_drop_write(f->f_path.mnt); +cleanup_inode: + put_write_access(f->f_inode); + return error; +} + static int do_dentry_open(struct file *f, struct inode *inode, int (*open)(struct inode *, struct file *)) @@ -782,14 +806,9 @@ static int do_dentry_open(struct file *f, if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) { i_readcount_inc(inode); } else if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) { - error = get_write_access(inode); + error = file_get_write_access(f); if (unlikely(error)) goto cleanup_file; - error = __mnt_want_write(f->f_path.mnt); - if (unlikely(error)) { - put_write_access(inode); - goto cleanup_file; - } f->f_mode |= FMODE_WRITER; } -- 2.18.0.huawei.25
From: Amir Goldstein <amir73il@gmail.com> mainline inclusion from mainline-v6.7-rc1 commit 08582d678fcf11fc86188f0b92239d3d49667d8e category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15316 CVE: CVE-2026-46054 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- Overlayfs uses backing files with "fake" overlayfs f_path and "real" underlying f_inode, in order to use underlying inode aops for mapped files and to display the overlayfs path in /proc/<pid>/maps. In preparation for storing the overlayfs "fake" path instead of the underlying "real" path in struct backing_file, define a noop helper file_user_path() that returns f_path for now. Use the new helper in procfs and kernel logs whenever a path of a mapped file is displayed to users. Signed-off-by: Amir Goldstein <amir73il@gmail.com> Link: https://lore.kernel.org/r/20231009153712.1566422-3-amir73il@gmail.com Signed-off-by: Christian Brauner <brauner@kernel.org> Conflicts: arch/arc/kernel/troubleshoot.c fs/proc/task_mmu.c fs/proc/task_nommu.c [fs/proc/task_mmu.c: since commit d09e8ca6cb93 ("mm: anonymous shared memory naming") is not backport, it does not show anon_shmem for anon_name. Others are only context conflicts.] Signed-off-by: Cai Xinchen <caixinchen1@huawei.com> --- arch/arc/kernel/troubleshoot.c | 6 ++++-- fs/proc/base.c | 2 +- fs/proc/nommu.c | 2 +- fs/proc/task_mmu.c | 4 ++-- fs/proc/task_nommu.c | 2 +- include/linux/fs.h | 14 ++++++++++++++ kernel/trace/trace_output.c | 2 +- 7 files changed, 24 insertions(+), 8 deletions(-) diff --git a/arch/arc/kernel/troubleshoot.c b/arch/arc/kernel/troubleshoot.c index a331bb5d8319..66d83a3eaca0 100644 --- a/arch/arc/kernel/troubleshoot.c +++ b/arch/arc/kernel/troubleshoot.c @@ -90,10 +90,12 @@ static void show_faulting_vma(unsigned long address) */ if (vma && (vma->vm_start <= address)) { char buf[ARC_PATH_MAX]; - char *nm = "?"; + char *nm = "anon"; if (vma->vm_file) { - nm = file_path(vma->vm_file, buf, ARC_PATH_MAX-1); + /* XXX: can we use %pD below and get rid of buf? */ + nm = d_path(file_user_path(vma->vm_file), buf, + ARC_PATH_MAX-1); if (IS_ERR(nm)) nm = "?"; } diff --git a/fs/proc/base.c b/fs/proc/base.c index 2b3719da025b..fe83f4d8517b 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -2421,7 +2421,7 @@ static int map_files_get_link(struct dentry *dentry, struct path *path) rc = -ENOENT; vma = find_exact_vma(mm, vm_start, vm_end); if (vma && vma->vm_file) { - *path = vma->vm_file->f_path; + *path = *file_user_path(vma->vm_file); path_get(path); rc = 0; } diff --git a/fs/proc/nommu.c b/fs/proc/nommu.c index 13452b32e2bd..b7e06be41224 100644 --- a/fs/proc/nommu.c +++ b/fs/proc/nommu.c @@ -59,7 +59,7 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region) if (file) { seq_pad(m, ' '); - seq_file_path(m, file, ""); + seq_path(m, file_user_path(file), ""); } seq_putc(m, '\n'); diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 2dbc954ec8e7..d5281d51b944 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -397,7 +397,7 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma) */ if (file) { seq_pad(m, ' '); - seq_file_path(m, file, "\n"); + seq_path(m, file_user_path(file), "\n"); goto done; } @@ -2386,7 +2386,7 @@ static int show_numa_map(struct seq_file *m, void *v) if (file) { seq_puts(m, " file="); - seq_file_path(m, file, "\n\t= "); + seq_path(m, file_user_path(file), "\n\t= "); } else if (vma->vm_start <= mm->brk && vma->vm_end >= mm->start_brk) { seq_puts(m, " heap"); } else if (is_stack(vma)) { diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c index 97f387d30e74..a88d8bfa3c25 100644 --- a/fs/proc/task_nommu.c +++ b/fs/proc/task_nommu.c @@ -175,7 +175,7 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma) if (file) { seq_pad(m, ' '); - seq_file_path(m, file, ""); + seq_path(m, file_user_path(file), ""); } else if (mm && is_stack(vma)) { seq_pad(m, ' '); seq_puts(m, "[stack]"); diff --git a/include/linux/fs.h b/include/linux/fs.h index 9cea290c15fb..aa3448ab6101 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2741,6 +2741,20 @@ static inline const struct path *file_real_path(struct file *f) return &f->f_path; } +/* + * file_user_path - get the path to display for memory mapped file + * + * When mmapping a file on a stackable filesystem (e.g., overlayfs), the file + * stored in ->vm_file is a backing file whose f_inode is on the underlying + * filesystem. When the mapped file path is displayed to user (e.g. via + * /proc/<pid>/maps), this helper should be used to get the path to display + * to the user, which is the path of the fd that user has requested to map. + */ +static inline const struct path *file_user_path(struct file *f) +{ + return &f->f_path; +} + static inline struct file *file_clone_open(struct file *file) { return dentry_open(&file->f_path, file->f_flags, file->f_cred); diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index 74adb873e2aa..5c598bf9bc55 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -400,7 +400,7 @@ static int seq_print_user_ip(struct trace_seq *s, struct mm_struct *mm, vmstart = vma->vm_start; } if (file) { - ret = trace_seq_path(s, &file->f_path); + ret = trace_seq_path(s, file_user_path(file)); if (ret) trace_seq_printf(s, "[+0x%lx]", ip - vmstart); -- 2.18.0.huawei.25
From: Amir Goldstein <amir73il@gmail.com> mainline inclusion from mainline-v6.7-rc1 commit def3ae83da02f87005210fa3d448c5dd37ba4105 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15316 CVE: CVE-2026-46054 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- A backing file struct stores two path's, one "real" path that is referring to f_inode and one "fake" path, which should be displayed to users in /proc/<pid>/maps. There is a lot more potential code that needs to know the "real" path, then code that needs to know the "fake" path. Instead of code having to request the "real" path with file_real_path(), store the "real" path in f_path and require code that needs to know the "fake" path request it with file_user_path(). Replace the file_real_path() helper with a simple const accessor f_path(). After this change, file_dentry() is not expected to observe any files with overlayfs f_path and real f_inode, so the call to ->d_real() should not be needed. Leave the ->d_real() call for now and add an assertion in ovl_d_real() to catch if we made wrong assumptions. Suggested-by: Miklos Szeredi <miklos@szeredi.hu> Link: https://lore.kernel.org/r/CAJfpegtt48eXhhjDFA1ojcHPNKj3Go6joryCPtEFAKpocyBsn... Signed-off-by: Amir Goldstein <amir73il@gmail.com> Link: https://lore.kernel.org/r/20231009153712.1566422-4-amir73il@gmail.com Signed-off-by: Christian Brauner <brauner@kernel.org> Conflicts: fs/file_table.c fs/internal.h fs/open.c fs/overlayfs/super.c include/linux/fsnotify.h [fs/internal.h: 5.10 use __mnt_drop_write instead of mnt_put_write_access. fs/open.c: use __mnt_want_write instead of mnt_get_write_access. include/linux/fsnotify.h: 5.10 already use flie->f_path instead of file_real_path Others are only context conflicts.] Signed-off-by: Cai Xinchen <caixinchen1@huawei.com> --- fs/file_table.c | 12 ++++++------ fs/internal.h | 2 +- fs/open.c | 23 +++++++++++------------ fs/overlayfs/super.c | 16 ++++++++++++---- include/linux/fs.h | 22 ++++------------------ 5 files changed, 34 insertions(+), 41 deletions(-) diff --git a/fs/file_table.c b/fs/file_table.c index 3d27e490d163..4b44beb28844 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -73,10 +73,10 @@ static inline struct file *kmem_cache_zalloc_file(void) } #endif -/* Container for backing file with optional real path */ +/* Container for backing file with optional user path */ struct backing_file { struct file file; - struct path real_path; + struct path user_path; }; static inline struct backing_file *backing_file(struct file *f) @@ -84,11 +84,11 @@ static inline struct backing_file *backing_file(struct file *f) return container_of(f, struct backing_file, file); } -struct path *backing_file_real_path(struct file *f) +struct path *backing_file_user_path(struct file *f) { - return &backing_file(f)->real_path; + return &backing_file(f)->user_path; } -EXPORT_SYMBOL_GPL(backing_file_real_path); +EXPORT_SYMBOL_GPL(backing_file_user_path); static void file_free_rcu(struct rcu_head *head) { @@ -105,7 +105,7 @@ static inline void file_free(struct file *f) { security_file_free(f); if (unlikely(f->f_mode & FMODE_BACKING)) - path_put(backing_file_real_path(f)); + path_put(backing_file_user_path(f)); if (likely(!(f->f_mode & FMODE_NOACCOUNT))) percpu_counter_dec(&nr_files); call_rcu(&f->f_u.fu_rcuhead, file_free_rcu); diff --git a/fs/internal.h b/fs/internal.h index c143ffd1a174..ee3fd9dcad13 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -116,7 +116,7 @@ static inline void file_put_write_access(struct file *file) put_write_access(file->f_inode); __mnt_drop_write(file->f_path.mnt); if (unlikely(file->f_mode & FMODE_BACKING)) - __mnt_drop_write(backing_file_real_path(file)->mnt); + __mnt_drop_write(backing_file_user_path(file)->mnt); } static inline void put_file_access(struct file *file) diff --git a/fs/open.c b/fs/open.c index 16a11f8d6116..f056e3b3e114 100644 --- a/fs/open.c +++ b/fs/open.c @@ -771,7 +771,7 @@ static inline int file_get_write_access(struct file *f) if (unlikely(error)) goto cleanup_inode; if (unlikely(f->f_mode & FMODE_BACKING)) { - error = __mnt_want_write(backing_file_real_path(f)->mnt); + error = __mnt_want_write(backing_file_user_path(f)->mnt); if (unlikely(error)) goto cleanup_mnt; } @@ -972,20 +972,19 @@ EXPORT_SYMBOL(dentry_open); /** * backing_file_open - open a backing file for kernel internal use - * @path: path of the file to open + * @user_path: path that the user reuqested to open * @flags: open flags * @path: path of the backing file * @cred: credentials for open * * Open a backing file for a stackable filesystem (e.g., overlayfs). - * @path may be on the stackable filesystem and backing inode on the - * underlying filesystem. In this case, we want to be able to return - * the @real_path of the backing inode. This is done by embedding the - * returned file into a container structure that also stores the path of - * the backing inode on the underlying filesystem, which can be - * retrieved using backing_file_real_path(). + * @user_path may be on the stackable filesystem and @real_path on the + * underlying filesystem. In this case, we want to be able to return the + * @user_path of the stackable filesystem. This is done by embedding the + * returned file into a container structure that also stores the stacked + * file's path, which can be retrieved using backing_file_user_path(). */ -struct file *backing_file_open(const struct path *path, int flags, +struct file *backing_file_open(const struct path *user_path, int flags, const struct path *real_path, const struct cred *cred) { @@ -996,9 +995,9 @@ struct file *backing_file_open(const struct path *path, int flags, if (IS_ERR(f)) return f; - f->f_path = *path; - path_get(real_path); - *backing_file_real_path(f) = *real_path; + path_get(user_path); + *backing_file_user_path(f) = *user_path; + f->f_path = *real_path; error = do_dentry_open(f, d_inode(real_path->dentry), NULL); if (error) { fput(f); diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 5d7df839902d..eab146452e9b 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -81,14 +81,22 @@ static struct dentry *ovl_d_real(struct dentry *dentry, { struct dentry *real = NULL, *lower; - /* It's an overlay file */ + /* + * vfs is only expected to call d_real() with NULL from d_real_inode() + * and with overlay inode from file_dentry() on an overlay file. + * + * TODO: remove @inode argument from d_real() API, remove code in this + * function that deals with non-NULL @inode and remove d_real() call + * from file_dentry(). + */ if (inode && d_inode(dentry) == inode) return dentry; + else if (inode) + goto bug; if (!d_is_reg(dentry)) { - if (!inode || inode == d_inode(dentry)) - return dentry; - goto bug; + /* d_real_inode() is only relevant for regular files */ + return dentry; } real = ovl_dentry_upper(dentry); diff --git a/include/linux/fs.h b/include/linux/fs.h index aa3448ab6101..617574e86336 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2720,26 +2720,10 @@ struct file *dentry_open(const struct path *path, int flags, const struct cred *creds); struct file *dentry_create(const struct path *path, int flags, umode_t mode, const struct cred *cred); -struct file *backing_file_open(const struct path *path, int flags, +struct file *backing_file_open(const struct path *user_path, int flags, const struct path *real_path, const struct cred *cred); -struct path *backing_file_real_path(struct file *f); - -/* - * file_real_path - get the path corresponding to f_inode - * - * When opening a backing file for a stackable filesystem (e.g., - * overlayfs) f_path may be on the stackable filesystem and f_inode on - * the underlying filesystem. When the path associated with f_inode is - * needed, this helper should be used instead of accessing f_path - * directly. -*/ -static inline const struct path *file_real_path(struct file *f) -{ - if (unlikely(f->f_mode & FMODE_BACKING)) - return backing_file_real_path(f); - return &f->f_path; -} +struct path *backing_file_user_path(struct file *f); /* * file_user_path - get the path to display for memory mapped file @@ -2752,6 +2736,8 @@ static inline const struct path *file_real_path(struct file *f) */ static inline const struct path *file_user_path(struct file *f) { + if (unlikely(f->f_mode & FMODE_BACKING)) + return backing_file_user_path(f); return &f->f_path; } -- 2.18.0.huawei.25
From: André Almeida <andrealmeid@igalia.com> mainline inclusion from mainline-v6.16-rc1 commit 924577e4f6ca473de1528953a0e13505fae61d7b category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15316 CVE: CVE-2026-46054 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- When the lowerdir of an overlayfs is a merged directory of another overlayfs, ovl_open_realfile() will fail to open the real file and point to a lower dentry copy, without the proper parent path. After this, d_path() will then display the path incorrectly as if the file is placed in the root directory. This bug can be triggered with the following setup: mkdir -p ovl-A/lower ovl-A/upper ovl-A/merge ovl-A/work mkdir -p ovl-B/upper ovl-B/merge ovl-B/work cp /bin/cat ovl-A/lower/ mount -t overlay overlay -o \ lowerdir=ovl-A/lower,upperdir=ovl-A/upper,workdir=ovl-A/work \ ovl-A/merge mount -t overlay overlay -o \ lowerdir=ovl-A/merge,upperdir=ovl-B/upper,workdir=ovl-B/work \ ovl-B/merge ovl-A/merge/cat /proc/self/maps | grep --color cat ovl-B/merge/cat /proc/self/maps | grep --color cat The first cat will correctly show `/ovl-A/merge/cat`, while the second one shows just `/cat`. To fix that, uses file_user_path() inside of backing_file_open() to get the correct file path for the dentry. Co-developed-by: John Schoenick <johns@valvesoftware.com> Signed-off-by: John Schoenick <johns@valvesoftware.com> Signed-off-by: André Almeida <andrealmeid@igalia.com> Fixes: def3ae83da02 ("fs: store real path instead of fake path in backing file f_path") Cc: <stable@vger.kernel.org> # v6.7 Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> Conflicts: fs/overlayfs/file.c [Only context conflicts.] Signed-off-by: Cai Xinchen <caixinchen1@huawei.com> --- fs/overlayfs/file.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 460748b63806..f0e0d7904150 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -58,8 +58,8 @@ static struct file *ovl_open_realfile(const struct file *file, if (!inode_owner_or_capable(realinode)) flags &= ~O_NOATIME; - realfile = backing_file_open(&file->f_path, flags, realpath, - current_cred()); + realfile = backing_file_open(file_user_path((struct file *) file), + flags, realpath, current_cred()); } revert_creds(old_cred); -- 2.18.0.huawei.25
From: Adrian Hunter <adrian.hunter@intel.com> mainline inclusion from mainline-v6.18-rc2 commit 8818f507a9391019a3ec7c57b1a32e4b386e48a5 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15316 CVE: CVE-2026-46054 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- Some file systems like FUSE-based ones or overlayfs may record the backing file in struct vm_area_struct vm_file, instead of the user file that the user mmapped. Since commit def3ae83da02f ("fs: store real path instead of fake path in backing file f_path"), file_path() no longer returns the user file path when applied to a backing file. There is an existing helper file_user_path() for that situation. Use file_user_path() instead of file_path() to get the path for MMAP and MMAP2 events. Example: Setup: # cd /root # mkdir test ; cd test ; mkdir lower upper work merged # cp `which cat` lower # mount -t overlay overlay -olowerdir=lower,upperdir=upper,workdir=work merged # perf record -e intel_pt//u -- /root/test/merged/cat /proc/self/maps ... 55b0ba399000-55b0ba434000 r-xp 00018000 00:1a 3419 /root/test/merged/cat ... [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.060 MB perf.data ] # Before: File name is wrong (/cat), so decoding fails: # perf script --no-itrace --show-mmap-events cat 367 [016] 100.491492: PERF_RECORD_MMAP2 367/367: [0x55b0ba399000(0x9b000) @ 0x18000 00:02 3419 489959280]: r-xp /cat ... # perf script --itrace=e | wc -l Warning: 19 instruction trace errors 19 # After: File name is correct (/root/test/merged/cat), so decoding is ok: # perf script --no-itrace --show-mmap-events cat 364 [016] 72.153006: PERF_RECORD_MMAP2 364/364: [0x55ce4003d000(0x9b000) @ 0x18000 00:02 3419 3132534314]: r-xp /root/test/merged/cat # perf script --itrace=e # perf script --itrace=e | wc -l 0 # Fixes: def3ae83da02f ("fs: store real path instead of fake path in backing file f_path") Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Acked-by: Amir Goldstein <amir73il@gmail.com> Cc: stable@vger.kernel.org Conflicts: kernel/events/core.c [Only context conflicts.] Signed-off-by: Cai Xinchen <caixinchen1@huawei.com> --- kernel/events/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 13b04c042a70..e83fb01f8374 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -8152,7 +8152,7 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event) * need to add enough zero bytes after the string to handle * the 64bit alignment we do later. */ - name = file_path(file, buf, PATH_MAX - sizeof(u64)); + name = d_path(file_user_path(file), buf, PATH_MAX - sizeof(u64)); if (IS_ERR(name)) { name = "//toolong"; goto cpy_name; -- 2.18.0.huawei.25
From: Amir Goldstein <amir73il@gmail.com> mainline inclusion from mainline-v6.17-rc1 commit 4e301d858af17ae2ce56886296e5458c5a08219a category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15316 CVE: CVE-2026-46054 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- Add internal helper backing_file_set_user_path() for the only two cases that need to modify backing_file fields. Signed-off-by: Amir Goldstein <amir73il@gmail.com> Link: https://lore.kernel.org/20250607115304.2521155-2-amir73il@gmail.com Signed-off-by: Christian Brauner <brauner@kernel.org> Conflicts: fs/backing-file.c fs/file_table.c fs/open.c include/linux/fs.h fs/internal.h [fs/backing-file.c: There is no backing-file.c in 5.10, change the function in fs/open.c. There is no backing_tmpfile_open in 5.10. include/linux/fs.h: There is no file_user_inode function. Others are only context conflicts.] Signed-off-by: Cai Xinchen <caixinchen1@huawei.com> --- fs/file_table.c | 13 ++++++++----- fs/internal.h | 1 + fs/open.c | 2 +- include/linux/fs.h | 4 ++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/fs/file_table.c b/fs/file_table.c index 4b44beb28844..9af699732a21 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -79,17 +79,20 @@ struct backing_file { struct path user_path; }; -static inline struct backing_file *backing_file(struct file *f) -{ - return container_of(f, struct backing_file, file); -} +#define backing_file(f) container_of(f, struct backing_file, file) -struct path *backing_file_user_path(struct file *f) +struct path *backing_file_user_path(const struct file *f) { return &backing_file(f)->user_path; } EXPORT_SYMBOL_GPL(backing_file_user_path); +void backing_file_set_user_path(struct file *f, const struct path *path) +{ + backing_file(f)->user_path = *path; +} +EXPORT_SYMBOL_GPL(backing_file_set_user_path); + static void file_free_rcu(struct rcu_head *head) { struct file *f = container_of(head, struct file, f_u.fu_rcuhead); diff --git a/fs/internal.h b/fs/internal.h index ee3fd9dcad13..71a0a155ec8f 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -110,6 +110,7 @@ extern void chroot_fs_refs(const struct path *, const struct path *); struct file *alloc_empty_file(int flags, const struct cred *cred); struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred); struct file *alloc_empty_backing_file(int flags, const struct cred *cred); +void backing_file_set_user_path(struct file *f, const struct path *path); static inline void file_put_write_access(struct file *file) { diff --git a/fs/open.c b/fs/open.c index f056e3b3e114..df3066e5d87a 100644 --- a/fs/open.c +++ b/fs/open.c @@ -996,7 +996,7 @@ struct file *backing_file_open(const struct path *user_path, int flags, return f; path_get(user_path); - *backing_file_user_path(f) = *user_path; + backing_file_set_user_path(f, user_path); f->f_path = *real_path; error = do_dentry_open(f, d_inode(real_path->dentry), NULL); if (error) { diff --git a/include/linux/fs.h b/include/linux/fs.h index 617574e86336..d90265b6c61d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2723,7 +2723,7 @@ struct file *dentry_create(const struct path *path, int flags, umode_t mode, struct file *backing_file_open(const struct path *user_path, int flags, const struct path *real_path, const struct cred *cred); -struct path *backing_file_user_path(struct file *f); +struct path *backing_file_user_path(const struct file *f); /* * file_user_path - get the path to display for memory mapped file @@ -2734,7 +2734,7 @@ struct path *backing_file_user_path(struct file *f); * /proc/<pid>/maps), this helper should be used to get the path to display * to the user, which is the path of the fd that user has requested to map. */ -static inline const struct path *file_user_path(struct file *f) +static inline const struct path *file_user_path(const struct file *f) { if (unlikely(f->f_mode & FMODE_BACKING)) return backing_file_user_path(f); -- 2.18.0.huawei.25
From: Amir Goldstein <amir73il@gmail.com> stable inclusion from stable-v6.6.144 commit ba3ebdd89fa2071639dedd426c933f422a20dec6 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15316 CVE: CVE-2026-46054 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- [ Upstream commit 880bd496ec72a6dcb00cb70c430ef752ba242ae7 ] In preparation to adding LSM blob to backing_file struct, factor out helpers init_backing_file() and backing_file_free(). Cc: stable@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org Cc: linux-unionfs@vger.kernel.org Cc: linux-erofs@lists.ozlabs.org Signed-off-by: Amir Goldstein <amir73il@gmail.com> Reviewed-by: Serge Hallyn <serge@hallyn.com> [PM: use the term "LSM blob", fix comment style to match file] Signed-off-by: Paul Moore <paul@paul-moore.com> [1. The commit def3ae83da02 ("fs: store real path instead of fake path in backing file f_path") is not merged, The 6.6 LTS version accordingly operates on &ff->real_path instead of &ff->user_path. 2. Mainline's file_free() does both the backing_file cleanup and the kmem_cache_free() synchronously. Linux 6.6.y defers the actual kfree() to file_free_rcu() via call_rcu(), so only path_put() is done synchronously in file_free().] Signed-off-by: Cai Xinchen <caixinchen1@huawei.com> Signed-off-by: Sasha Levin <sashal@kernel.org> Conflicts: fs/file_table.c [commit def3ae83da02 ("fs: store real path instead of fake path in backing file f_path") is backport, use user_path instead of real_path] Signed-off-by: Cai Xinchen <caixinchen1@huawei.com> --- fs/file_table.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/fs/file_table.c b/fs/file_table.c index 9af699732a21..760fdc35899d 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -104,11 +104,16 @@ static void file_free_rcu(struct rcu_head *head) kmem_cache_free(filp_cachep, GET_FILE_WRAP(f)); } +static inline void backing_file_free(struct backing_file *ff) +{ + path_put(&ff->user_path); +} + static inline void file_free(struct file *f) { security_file_free(f); if (unlikely(f->f_mode & FMODE_BACKING)) - path_put(backing_file_user_path(f)); + backing_file_free(backing_file(f)); if (likely(!(f->f_mode & FMODE_NOACCOUNT))) percpu_counter_dec(&nr_files); call_rcu(&f->f_u.fu_rcuhead, file_free_rcu); @@ -249,6 +254,12 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred) return f; } +static int init_backing_file(struct backing_file *ff) +{ + memset(&ff->user_path, 0, sizeof(ff->user_path)); + return 0; +} + /* * Variant of alloc_empty_file() that allocates a backing_file container * and doesn't check and modify nr_files. @@ -271,7 +282,14 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred) return ERR_PTR(error); } + /* The f_mode flags must be set before fput(). */ ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT; + error = init_backing_file(ff); + if (unlikely(error)) { + fput(&ff->file); + return ERR_PTR(error); + } + return &ff->file; } -- 2.18.0.huawei.25
From: Paul Moore <paul@paul-moore.com> stable inclusion from stable-v6.6.144 commit 41c5b269af8b1f0bffcab7766a793f294ae6764e category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15316 CVE: CVE-2026-46054 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- [ Upstream commit 6af36aeb147a06dea47c49859cd6ca5659aeb987 ] Stacked filesystems such as overlayfs do not currently provide the necessary mechanisms for LSMs to properly enforce access controls on the mmap() and mprotect() operations. In order to resolve this gap, a LSM security blob is being added to the backing_file struct and the following new LSM hooks are being created: security_backing_file_alloc() security_backing_file_free() security_mmap_backing_file() The first two hooks are to manage the lifecycle of the LSM security blob in the backing_file struct, while the third provides a new mmap() access control point for the underlying backing file. It is also expected that LSMs will likely want to update their security_file_mprotect() callback to address issues with their mprotect() controls, but that does not require a change to the security_file_mprotect() LSM hook. There are a three other small changes to support these new LSM hooks: * Pass the user file associated with a backing file down to alloc_empty_backing_file() so it can be included in the security_backing_file_alloc() hook. * Add getter and setter functions for the backing_file struct LSM blob as the backing_file struct remains private to fs/file_table.c. * Constify the file struct field in the LSM common_audit_data struct to better support LSMs that need to pass a const file struct pointer into the common LSM audit code. Thanks to Arnd Bergmann for identifying the missing EXPORT_SYMBOL_GPL() and supplying a fixup. Cc: stable@vger.kernel.org Cc: linux-fsdevel@vger.kernel.org Cc: linux-unionfs@vger.kernel.org Cc: linux-erofs@lists.ozlabs.org Reviewed-by: Amir Goldstein <amir73il@gmail.com> Reviewed-by: Serge Hallyn <serge@hallyn.com> Reviewed-by: Christian Brauner <brauner@kernel.org> Signed-off-by: Paul Moore <paul@paul-moore.com> [1. Mainline uses call_int_hook(FUNC, ...) with the default IRC baked into the macro. Linux 6.6.y uses call_int_hook(FUNC, IRC, ...) requiring an explicit default return value. 2. fs/backing-file.c does not exist in LTS Linux 6.6.y places backing_file_open() in fs/open.c and lacks a dedicated fs/backing-file.c. The backing_file_mmap() function and scoped_with_creds() do not exist in 6.6.y. Therefore the LTS patch calls security_mmap_backing_file() directly in ovl_mmap() in fs/overlayfs/file.c rather than modifying backing_file_mmap(). 3. Missing filesystems/modules Linux 6.6.y does not have backing_tmpfile_open(), fs/fuse/passthrough.c, or the erofs ishare mmap path that the mainline patch touches. These hunks are dropped in the 6.6 LTS backport. 4. Use macro backing_file to replace inline function to eliminate the const warning.] Signed-off-by: Cai Xinchen <caixinchen1@huawei.com> Signed-off-by: Sasha Levin <sashal@kernel.org> Conflicts: fs/file_table.c fs/internal.h fs/open.c fs/overlayfs/file.c include/linux/fs.h include/linux/lsm_hooks.h include/linux/lsm_audit.h security/security.c [fs/open.c: in ovl_open_realfile, we backport 924577e4f6ca ("ovl: Fix nested backing file paths"), do as mainline. fs/overlayfs/file.c: In 5.10 ovl_mmap, we should fput(realfile) in sescurity_mmap_backing_file failed. in the function backing_file_open, the commit def3ae83da02 ("fs: store real path instead of fake path in backing file f_path") is backport, do as mainline. Others are only context conflicts.] Signed-off-by: Cai Xinchen <caixinchen1@huawei.com> --- fs/file_table.c | 27 +++++++-- fs/internal.h | 3 +- fs/open.c | 5 +- fs/overlayfs/file.c | 12 +++- include/linux/fs.h | 15 ++++- include/linux/lsm_audit.h | 2 +- include/linux/lsm_hook_defs.h | 5 ++ include/linux/lsm_hooks.h | 1 + include/linux/security.h | 22 +++++++ security/security.c | 110 ++++++++++++++++++++++++++++++++++ 10 files changed, 191 insertions(+), 11 deletions(-) diff --git a/fs/file_table.c b/fs/file_table.c index 760fdc35899d..b4cacac62cce 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -77,6 +77,9 @@ static inline struct file *kmem_cache_zalloc_file(void) struct backing_file { struct file file; struct path user_path; +#ifdef CONFIG_SECURITY + void *security; +#endif }; #define backing_file(f) container_of(f, struct backing_file, file) @@ -104,8 +107,21 @@ static void file_free_rcu(struct rcu_head *head) kmem_cache_free(filp_cachep, GET_FILE_WRAP(f)); } +#ifdef CONFIG_SECURITY +void *backing_file_security(const struct file *f) +{ + return backing_file(f)->security; +} + +void backing_file_set_security(struct file *f, void *security) +{ + backing_file(f)->security = security; +} +#endif /* CONFIG_SECURITY */ + static inline void backing_file_free(struct backing_file *ff) { + security_backing_file_free(&ff->file); path_put(&ff->user_path); } @@ -254,10 +270,12 @@ struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred) return f; } -static int init_backing_file(struct backing_file *ff) +static int init_backing_file(struct backing_file *ff, + const struct file *user_file) { memset(&ff->user_path, 0, sizeof(ff->user_path)); - return 0; + backing_file_set_security(&ff->file, NULL); + return security_backing_file_alloc(&ff->file, user_file); } /* @@ -267,7 +285,8 @@ static int init_backing_file(struct backing_file *ff) * This is only for kernel internal use, and the allocate file must not be * installed into file tables or such. */ -struct file *alloc_empty_backing_file(int flags, const struct cred *cred) +struct file *alloc_empty_backing_file(int flags, const struct cred *cred, + const struct file *user_file) { struct backing_file *ff; int error; @@ -284,7 +303,7 @@ struct file *alloc_empty_backing_file(int flags, const struct cred *cred) /* The f_mode flags must be set before fput(). */ ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT; - error = init_backing_file(ff); + error = init_backing_file(ff, user_file); if (unlikely(error)) { fput(&ff->file); return ERR_PTR(error); diff --git a/fs/internal.h b/fs/internal.h index 71a0a155ec8f..20974758595b 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -109,7 +109,8 @@ extern void chroot_fs_refs(const struct path *, const struct path *); */ struct file *alloc_empty_file(int flags, const struct cred *cred); struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred); -struct file *alloc_empty_backing_file(int flags, const struct cred *cred); +struct file *alloc_empty_backing_file(int flags, const struct cred *cred, + const struct file *user_file); void backing_file_set_user_path(struct file *f, const struct path *path); static inline void file_put_write_access(struct file *file) diff --git a/fs/open.c b/fs/open.c index df3066e5d87a..44ffd83872d0 100644 --- a/fs/open.c +++ b/fs/open.c @@ -984,14 +984,15 @@ EXPORT_SYMBOL(dentry_open); * returned file into a container structure that also stores the stacked * file's path, which can be retrieved using backing_file_user_path(). */ -struct file *backing_file_open(const struct path *user_path, int flags, +struct file *backing_file_open(const struct file *user_file, int flags, const struct path *real_path, const struct cred *cred) { + const struct path *user_path = &user_file->f_path; struct file *f; int error; - f = alloc_empty_backing_file(flags, cred); + f = alloc_empty_backing_file(flags, cred, user_file); if (IS_ERR(f)) return f; diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index f0e0d7904150..02427e6fdfb0 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -58,8 +58,8 @@ static struct file *ovl_open_realfile(const struct file *file, if (!inode_owner_or_capable(realinode)) flags &= ~O_NOATIME; - realfile = backing_file_open(file_user_path((struct file *) file), - flags, realpath, current_cred()); + realfile = backing_file_open(file, flags, realpath, + current_cred()); } revert_creds(old_cred); @@ -491,6 +491,7 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync) static int ovl_mmap(struct file *file, struct vm_area_struct *vma) { + struct file *user_file = vma->vm_file; struct file *realfile = file->private_data; const struct cred *old_cred; int ret; @@ -504,6 +505,13 @@ static int ovl_mmap(struct file *file, struct vm_area_struct *vma) vma->vm_file = get_file(realfile); old_cred = ovl_override_creds(file_inode(file)->i_sb); + ret = security_mmap_backing_file(vma, realfile, user_file); + if (ret) { + revert_creds(old_cred); + fput(realfile); + return ret; + } + ret = call_mmap(vma->vm_file, vma); revert_creds(old_cred); diff --git a/include/linux/fs.h b/include/linux/fs.h index d90265b6c61d..8ca24d2f79f9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2720,11 +2720,24 @@ struct file *dentry_open(const struct path *path, int flags, const struct cred *creds); struct file *dentry_create(const struct path *path, int flags, umode_t mode, const struct cred *cred); -struct file *backing_file_open(const struct path *user_path, int flags, +struct file *backing_file_open(const struct file *user_file, int flags, const struct path *real_path, const struct cred *cred); struct path *backing_file_user_path(const struct file *f); +#ifdef CONFIG_SECURITY +void *backing_file_security(const struct file *f); +void backing_file_set_security(struct file *f, void *security); +#else +static inline void *backing_file_security(const struct file *f) +{ + return NULL; +} +static inline void backing_file_set_security(struct file *f, void *security) +{ +} +#endif /* CONFIG_SECURITY */ + /* * file_user_path - get the path to display for memory mapped file * diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index 28f23b341c1c..8b42e110cb26 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -92,7 +92,7 @@ struct common_audit_data { #endif char *kmod_name; struct lsm_ioctlop_audit *op; - struct file *file; + const struct file *file; struct lsm_ibpkey_audit *ibpkey; struct lsm_ibendport_audit *ibendport; int reason; diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index eb15ba8b2c7d..0bb58214b130 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -157,6 +157,9 @@ LSM_HOOK(int, 0, kernfs_init_security, struct kernfs_node *kn_dir, LSM_HOOK(int, 0, file_permission, struct file *file, int mask) LSM_HOOK(int, 0, file_alloc_security, struct file *file) LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file) +LSM_HOOK(int, 0, backing_file_alloc, struct file *backing_file, + const struct file *user_file) +LSM_HOOK(void, LSM_RET_VOID, backing_file_free, struct file *backing_file) LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd, unsigned long arg) LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd, @@ -164,6 +167,8 @@ LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd, LSM_HOOK(int, 0, mmap_addr, unsigned long addr) LSM_HOOK(int, 0, mmap_file, struct file *file, unsigned long reqprot, unsigned long prot, unsigned long flags) +LSM_HOOK(int, 0, mmap_backing_file, struct vm_area_struct *vma, + struct file *backing_file, struct file *user_file) LSM_HOOK(int, 0, file_mprotect, struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot) LSM_HOOK(int, 0, file_lock, struct file *file, unsigned int cmd) diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 21b3c43cd05a..8cc442b95d5a 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1566,6 +1566,7 @@ struct security_hook_list { struct lsm_blob_sizes { int lbs_cred; int lbs_file; + int lbs_backing_file; int lbs_inode; int lbs_superblock; int lbs_ipc; diff --git a/include/linux/security.h b/include/linux/security.h index b9cd823992f5..3d80ee8e8eae 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -370,11 +370,17 @@ int security_kernfs_init_security(struct kernfs_node *kn_dir, int security_file_permission(struct file *file, int mask); int security_file_alloc(struct file *file); void security_file_free(struct file *file); +int security_backing_file_alloc(struct file *backing_file, + const struct file *user_file); +void security_backing_file_free(struct file *backing_file); int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg); int security_file_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg); int security_mmap_file(struct file *file, unsigned long prot, unsigned long flags); +int security_mmap_backing_file(struct vm_area_struct *vma, + struct file *backing_file, + struct file *user_file); int security_mmap_addr(unsigned long addr); int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot); @@ -927,6 +933,15 @@ static inline int security_file_alloc(struct file *file) static inline void security_file_free(struct file *file) { } +static inline int security_backing_file_alloc(struct file *backing_file, + const struct file *user_file) +{ + return 0; +} + +static inline void security_backing_file_free(struct file *backing_file) +{ } + static inline int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -946,6 +961,13 @@ static inline int security_mmap_file(struct file *file, unsigned long prot, return 0; } +static inline int security_mmap_backing_file(struct vm_area_struct *vma, + struct file *backing_file, + struct file *user_file) +{ + return 0; +} + static inline int security_mmap_addr(unsigned long addr) { return cap_mmap_addr(addr); diff --git a/security/security.c b/security/security.c index a86aa7f34ce2..a3370f188cb7 100644 --- a/security/security.c +++ b/security/security.c @@ -76,6 +76,7 @@ struct security_hook_heads security_hook_heads __lsm_ro_after_init; static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain); static struct kmem_cache *lsm_file_cache; +static struct kmem_cache *lsm_backing_file_cache; static struct kmem_cache *lsm_inode_cache; char *lsm_names; @@ -197,6 +198,8 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed) lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred); lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file); + lsm_set_blob_size(&needed->lbs_backing_file, + &blob_sizes.lbs_backing_file); /* * The inode blob gets an rcu_head in addition to * what the modules might need. @@ -339,6 +342,7 @@ static void __init ordered_lsm_init(void) init_debug("cred blob size = %d\n", blob_sizes.lbs_cred); init_debug("file blob size = %d\n", blob_sizes.lbs_file); + init_debug("backing_file blob size = %d\n", blob_sizes.lbs_backing_file); init_debug("inode blob size = %d\n", blob_sizes.lbs_inode); init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc); init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg); @@ -352,6 +356,11 @@ static void __init ordered_lsm_init(void) lsm_file_cache = kmem_cache_create("lsm_file_cache", blob_sizes.lbs_file, 0, SLAB_PANIC, NULL); + if (blob_sizes.lbs_backing_file) + lsm_backing_file_cache = kmem_cache_create( + "lsm_backing_file_cache", + blob_sizes.lbs_backing_file, + 0, SLAB_PANIC, NULL); if (blob_sizes.lbs_inode) lsm_inode_cache = kmem_cache_create("lsm_inode_cache", blob_sizes.lbs_inode, 0, @@ -577,6 +586,30 @@ static int lsm_file_alloc(struct file *file) return 0; } +/** + * lsm_backing_file_alloc - allocate a composite backing file blob + * @backing_file: the backing file + * + * Allocate the backing file blob for all the modules. + * + * Returns 0, or -ENOMEM if memory can't be allocated. + */ +static int lsm_backing_file_alloc(struct file *backing_file) +{ + void *blob; + + if (!lsm_backing_file_cache) { + backing_file_set_security(backing_file, NULL); + return 0; + } + + blob = kmem_cache_zalloc(lsm_backing_file_cache, GFP_KERNEL); + backing_file_set_security(backing_file, blob); + if (!blob) + return -ENOMEM; + return 0; +} + /** * lsm_inode_alloc - allocate a composite inode blob * @inode: the inode that needs a blob @@ -1545,6 +1578,57 @@ void security_file_free(struct file *file) } } +/** + * security_backing_file_alloc() - Allocate and setup a backing file blob + * @backing_file: the backing file + * @user_file: the associated user visible file + * + * Allocate a backing file LSM blob and perform any necessary initialization of + * the LSM blob. There will be some operations where the LSM will not have + * access to @user_file after this point, so any important state associated + * with @user_file that is important to the LSM should be captured in the + * backing file's LSM blob. + * + * LSM's should avoid taking a reference to @user_file in this hook as it will + * result in problems later when the system attempts to drop/put the file + * references due to a circular dependency. + * + * Return: Return 0 if the hook is successful, negative values otherwise. + */ +int security_backing_file_alloc(struct file *backing_file, + const struct file *user_file) +{ + int rc; + + rc = lsm_backing_file_alloc(backing_file); + if (rc) + return rc; + rc = call_int_hook(backing_file_alloc, 0, backing_file, user_file); + if (unlikely(rc)) + security_backing_file_free(backing_file); + + return rc; +} + +/** + * security_backing_file_free() - Free a backing file blob + * @backing_file: the backing file + * + * Free any LSM state associate with a backing file's LSM blob, including the + * blob itself. + */ +void security_backing_file_free(struct file *backing_file) +{ + void *blob = backing_file_security(backing_file); + + call_void_hook(backing_file_free, backing_file); + + if (blob) { + backing_file_set_security(backing_file, NULL); + kmem_cache_free(lsm_backing_file_cache, blob); + } +} + int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { return call_int_hook(file_ioctl, 0, file, cmd, arg); @@ -1614,6 +1698,32 @@ int security_mmap_file(struct file *file, unsigned long prot, return ima_file_mmap(file, prot, prot_adj, flags); } +/** + * security_mmap_backing_file - Check if mmap'ing a backing file is allowed + * @vma: the vm_area_struct for the mmap'd region + * @backing_file: the backing file being mmap'd + * @user_file: the user file being mmap'd + * + * Check permissions for a mmap operation on a stacked filesystem. This hook + * is called after the security_mmap_file() and is responsible for authorizing + * the mmap on @backing_file. It is important to note that the mmap operation + * on @user_file has already been authorized and the @vma->vm_file has been + * set to @backing_file. + * + * Return: Returns 0 if permission is granted. + */ +int security_mmap_backing_file(struct vm_area_struct *vma, + struct file *backing_file, + struct file *user_file) +{ + /* recommended by the stackable filesystem devs */ + if (WARN_ON_ONCE(!(backing_file->f_mode & FMODE_BACKING))) + return -EIO; + + return call_int_hook(mmap_backing_file, 0, vma, backing_file, user_file); +} +EXPORT_SYMBOL_GPL(security_mmap_backing_file); + int security_mmap_addr(unsigned long addr) { return call_int_hook(mmap_addr, 0, addr); -- 2.18.0.huawei.25
From: Paul Moore <paul@paul-moore.com> stable inclusion from stable-v6.12.95 commit 8bacd09f12c27710228562e4d13163e58c5f4a45 category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/15316 CVE: CVE-2026-46054 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=... -------------------------------- [ Upstream commit 82544d36b1729153c8aeb179e84750f0c085d3b1 ] The existing SELinux security model for overlayfs is to allow access if the current task is able to access the top level file (the "user" file) and the mounter's credentials are sufficient to access the lower level file (the "backing" file). Unfortunately, the current code does not properly enforce these access controls for both mmap() and mprotect() operations on overlayfs filesystems. This patch makes use of the newly created security_mmap_backing_file() LSM hook to provide the missing backing file enforcement for mmap() operations, and leverages the backing file API and new LSM blob to provide the necessary information to properly enforce the mprotect() access controls. Cc: stable@vger.kernel.org Acked-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Paul Moore <paul@paul-moore.com> Signed-off-by: Cai Xinchen <caixinchen1@huawei.com> Signed-off-by: Sasha Levin <sashal@kernel.org> Conflicts: security/selinux/hooks.c.rej security/selinux/include/objsec.h.rej [The function avc_has_perm use selinux_state as first input parameter in 5.10.] Signed-off-by: Cai Xinchen <caixinchen1@huawei.com> --- security/selinux/hooks.c | 242 ++++++++++++++++++++++-------- security/selinux/include/objsec.h | 11 ++ 2 files changed, 189 insertions(+), 64 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 0973cc29227f..af94d6bb55a4 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1697,50 +1697,71 @@ static inline int file_path_has_perm(const struct cred *cred, static int bpf_fd_pass(const struct file *file, u32 sid); #endif -/* Check whether a task can use an open file descriptor to - access an inode in a given way. Check access to the - descriptor itself, and then use dentry_has_perm to - check a particular permission to the file. - Access to the descriptor is implicitly granted if it - has the same SID as the process. If av is zero, then - access to the file is not checked, e.g. for cases - where only the descriptor is affected like seek. */ -static int file_has_perm(const struct cred *cred, - struct file *file, - u32 av) +static int __file_has_perm(const struct cred *cred, const struct file *file, + u32 av, bool bf_user_file) { - struct file_security_struct *fsec = selinux_file(file); - struct inode *inode = file_inode(file); struct common_audit_data ad; - u32 sid = cred_sid(cred); + struct inode *inode; + u32 ssid = cred_sid(cred); + u32 tsid_fd; int rc; - ad.type = LSM_AUDIT_DATA_FILE; - ad.u.file = file; + if (bf_user_file) { + struct backing_file_security_struct *bfsec; + const struct path *path; - if (sid != fsec->sid) { - rc = avc_has_perm(&selinux_state, - sid, fsec->sid, - SECCLASS_FD, - FD__USE, - &ad); + if (WARN_ON(!(file->f_mode & FMODE_BACKING))) + return -EIO; + + bfsec = selinux_backing_file(file); + path = backing_file_user_path(file); + tsid_fd = bfsec->uf_sid; + inode = d_inode(path->dentry); + + ad.type = LSM_AUDIT_DATA_PATH; + ad.u.path = *path; + } else { + struct file_security_struct *fsec = selinux_file(file); + + tsid_fd = fsec->sid; + inode = file_inode(file); + + ad.type = LSM_AUDIT_DATA_FILE; + ad.u.file = file; + } + + if (ssid != tsid_fd) { + rc = avc_has_perm(&selinux_state, ssid, tsid_fd, SECCLASS_FD, FD__USE, &ad); if (rc) - goto out; + return rc; } #ifdef CONFIG_BPF_SYSCALL - rc = bpf_fd_pass(file, cred_sid(cred)); + /* regardless of backing vs user file, use the underlying file here */ + rc = bpf_fd_pass(file, ssid); if (rc) return rc; #endif /* av is zero if only checking access to the descriptor. */ - rc = 0; if (av) - rc = inode_has_perm(cred, inode, av, &ad); + return inode_has_perm(cred, inode, av, &ad); -out: - return rc; + return 0; +} + +/* Check whether a task can use an open file descriptor to + access an inode in a given way. Check access to the + descriptor itself, and then use dentry_has_perm to + check a particular permission to the file. + Access to the descriptor is implicitly granted if it + has the same SID as the process. If av is zero, then + access to the file is not checked, e.g. for cases + where only the descriptor is affected like seek. */ +static inline int file_has_perm(const struct cred *cred, + const struct file *file, u32 av) +{ + return __file_has_perm(cred, file, av, false); } /* @@ -3558,6 +3579,17 @@ static int selinux_file_alloc_security(struct file *file) return 0; } +static int selinux_backing_file_alloc(struct file *backing_file, + const struct file *user_file) +{ + struct backing_file_security_struct *bfsec; + + bfsec = selinux_backing_file(backing_file); + bfsec->uf_sid = selinux_file(user_file)->sid; + + return 0; +} + /* * Check whether a task has the ioctl permission and cmd * operation to an inode. @@ -3677,43 +3709,56 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd, static int default_noexec __ro_after_init; -static int file_map_prot_check(struct file *file, unsigned long prot, int shared) +static int __file_map_prot_check(const struct cred *cred, + const struct file *file, unsigned long prot, + bool shared, bool bf_user_file) { - const struct cred *cred = current_cred(); - u32 sid = cred_sid(cred); - int rc = 0; + struct inode *inode = NULL; + bool prot_exec = prot & PROT_EXEC; + bool prot_write = prot & PROT_WRITE; + + if (file) { + if (bf_user_file) + inode = d_inode(backing_file_user_path(file)->dentry); + else + inode = file_inode(file); + } + + if (default_noexec && prot_exec && + (!file || IS_PRIVATE(inode) || (!shared && prot_write))) { + int rc; + u32 sid = cred_sid(cred); - if (default_noexec && - (prot & PROT_EXEC) && (!file || IS_PRIVATE(file_inode(file)) || - (!shared && (prot & PROT_WRITE)))) { /* - * We are making executable an anonymous mapping or a - * private file mapping that will also be writable. - * This has an additional check. + * We are making executable an anonymous mapping or a private + * file mapping that will also be writable. */ rc = avc_has_perm(&selinux_state, - sid, sid, SECCLASS_PROCESS, - PROCESS__EXECMEM, NULL); + sid, sid, SECCLASS_PROCESS, PROCESS__EXECMEM, + NULL); if (rc) - goto error; + return rc; } if (file) { - /* read access is always possible with a mapping */ + /* "read" always possible, "write" only if shared */ u32 av = FILE__READ; - - /* write access only matters if the mapping is shared */ - if (shared && (prot & PROT_WRITE)) + if (shared && prot_write) av |= FILE__WRITE; - - if (prot & PROT_EXEC) + if (prot_exec) av |= FILE__EXECUTE; - return file_has_perm(cred, file, av); + return __file_has_perm(cred, file, av, bf_user_file); } -error: - return rc; + return 0; +} + +static inline int file_map_prot_check(const struct cred *cred, + const struct file *file, + unsigned long prot, bool shared) +{ + return __file_map_prot_check(cred, file, prot, shared, false); } static int selinux_mmap_addr(unsigned long addr) @@ -3730,17 +3775,17 @@ static int selinux_mmap_addr(unsigned long addr) return rc; } -static int selinux_mmap_file(struct file *file, unsigned long reqprot, - unsigned long prot, unsigned long flags) +static int selinux_mmap_file_common(const struct cred *cred, struct file *file, + unsigned long reqprot, unsigned long prot, + bool shared) { - struct common_audit_data ad; - int rc; - if (file) { + int rc; + struct common_audit_data ad; + ad.type = LSM_AUDIT_DATA_FILE; ad.u.file = file; - rc = inode_has_perm(current_cred(), file_inode(file), - FILE__MAP, &ad); + rc = inode_has_perm(cred, file_inode(file), FILE__MAP, &ad); if (rc) return rc; } @@ -3748,36 +3793,86 @@ static int selinux_mmap_file(struct file *file, unsigned long reqprot, if (checkreqprot_get(&selinux_state)) prot = reqprot; - return file_map_prot_check(file, prot, - (flags & MAP_TYPE) == MAP_SHARED); + return file_map_prot_check(cred, file, prot, shared); +} + +static int selinux_mmap_file(struct file *file, + unsigned long reqprot, + unsigned long prot, unsigned long flags) +{ + return selinux_mmap_file_common(current_cred(), file, reqprot, prot, + (flags & MAP_TYPE) == MAP_SHARED); +} + +/** + * selinux_mmap_backing_file - Check mmap permissions on a backing file + * @vma: memory region + * @backing_file: stacked filesystem backing file + * @user_file: user visible file + * + * This is called after selinux_mmap_file() on stacked filesystems, and it + * is this function's responsibility to verify access to @backing_file and + * setup the SELinux state for possible later use in the mprotect() code path. + * + * By the time this function is called, mmap() access to @user_file has already + * been authorized and @vma->vm_file has been set to point to @backing_file. + * + * Return zero on success, negative values otherwise. + */ +static int selinux_mmap_backing_file(struct vm_area_struct *vma, + struct file *backing_file, + struct file *user_file __always_unused) +{ + unsigned long prot = 0; + + /* translate vma->vm_flags perms into PROT perms */ + if (vma->vm_flags & VM_READ) + prot |= PROT_READ; + if (vma->vm_flags & VM_WRITE) + prot |= PROT_WRITE; + if (vma->vm_flags & VM_EXEC) + prot |= PROT_EXEC; + + return selinux_mmap_file_common(backing_file->f_cred, backing_file, + prot, prot, vma->vm_flags & VM_SHARED); } static int selinux_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot) { + int rc; const struct cred *cred = current_cred(); u32 sid = cred_sid(cred); + const struct file *file = vma->vm_file; + bool backing_file; + bool shared = vma->vm_flags & VM_SHARED; + + /* check if we need to trigger the "backing files are awful" mode */ + backing_file = file && (file->f_mode & FMODE_BACKING); if (checkreqprot_get(&selinux_state)) prot = reqprot; if (default_noexec && (prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) { - int rc = 0; if (vma->vm_start >= vma->vm_mm->start_brk && vma->vm_end <= vma->vm_mm->brk) { rc = avc_has_perm(&selinux_state, sid, sid, SECCLASS_PROCESS, PROCESS__EXECHEAP, NULL); - } else if (!vma->vm_file && + if (rc) + return rc; + } else if (!file && ((vma->vm_start <= vma->vm_mm->start_stack && vma->vm_end >= vma->vm_mm->start_stack) || vma_is_stack_for_current(vma))) { rc = avc_has_perm(&selinux_state, sid, sid, SECCLASS_PROCESS, PROCESS__EXECSTACK, NULL); - } else if (vma->vm_file && vma->anon_vma) { + if (rc) + return rc; + } else if (file && vma->anon_vma) { /* * We are making executable a file mapping that has * had some COW done. Since pages might have been @@ -3785,13 +3880,29 @@ static int selinux_file_mprotect(struct vm_area_struct *vma, * modified content. This typically should only * occur for text relocations. */ - rc = file_has_perm(cred, vma->vm_file, FILE__EXECMOD); + rc = __file_has_perm(cred, file, FILE__EXECMOD, + backing_file); + if (rc) + return rc; + if (backing_file) { + rc = file_has_perm(file->f_cred, file, + FILE__EXECMOD); + if (rc) + return rc; + } } + } + + rc = __file_map_prot_check(cred, file, prot, shared, backing_file); + if (rc) + return rc; + if (backing_file) { + rc = file_map_prot_check(file->f_cred, file, prot, shared); if (rc) return rc; } - return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED); + return 0; } static int selinux_file_lock(struct file *file, unsigned int cmd) @@ -6909,6 +7020,7 @@ static int selinux_lockdown(enum lockdown_reason what) struct lsm_blob_sizes selinux_blob_sizes __lsm_ro_after_init = { .lbs_cred = sizeof(struct task_security_struct), .lbs_file = sizeof(struct file_security_struct), + .lbs_backing_file = sizeof(struct backing_file_security_struct), .lbs_inode = sizeof(struct inode_security_struct), .lbs_ipc = sizeof(struct ipc_security_struct), .lbs_msg_msg = sizeof(struct msg_security_struct), @@ -7060,9 +7172,11 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(file_permission, selinux_file_permission), LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security), + LSM_HOOK_INIT(backing_file_alloc, selinux_backing_file_alloc), LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl), LSM_HOOK_INIT(file_ioctl_compat, selinux_file_ioctl_compat), LSM_HOOK_INIT(mmap_file, selinux_mmap_file), + LSM_HOOK_INIT(mmap_backing_file, selinux_mmap_backing_file), LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr), LSM_HOOK_INIT(file_mprotect, selinux_file_mprotect), LSM_HOOK_INIT(file_lock, selinux_file_lock), diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index dcebd2b95ca7..847e13023467 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -60,6 +60,10 @@ struct file_security_struct { u32 pseqno; /* Policy seqno at the time of file open */ }; +struct backing_file_security_struct { + u32 uf_sid; /* associated user file fsec->sid */ +}; + struct superblock_security_struct { struct super_block *sb; /* back pointer to sb object */ u32 sid; /* SID of file system superblock */ @@ -159,6 +163,13 @@ static inline struct file_security_struct *selinux_file(const struct file *file) return file->f_security + selinux_blob_sizes.lbs_file; } +static inline struct backing_file_security_struct * +selinux_backing_file(const struct file *backing_file) +{ + void *blob = backing_file_security(backing_file); + return blob + selinux_blob_sizes.lbs_backing_file; +} + static inline struct inode_security_struct *selinux_inode( const struct inode *inode) { -- 2.18.0.huawei.25
participants (2)
-
Cai Xinchen -
patchwork bot