From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from canpmsgout06.his.huawei.com (canpmsgout06.his.huawei.com [113.46.200.221]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2A9A837CD34; Sat, 27 Jun 2026 06:29:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=113.46.200.221 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782541753; cv=none; b=d+b74jR8wZ/jku3GhnUTYsizOYO2ekw2abqGC6SxSAefMs3crv9i7mT57CWU1RRlLV6fw5/RoDmfExQzj7/ff4fEMV/VsLRIshOPeQI4Mhu1f3lfzeJrjjtljdvUjWLDQIs1uhKf18Us73ZzmHe5J2Mhhapi3qUbWYwyO1UWQnc= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782541753; c=relaxed/simple; bh=0U76SdgWofmXOiD7EFVgc6edfdKQNwOs+n/Z/pvXWeY=; h=From:To:CC:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=kCrvbrPIOExqRb7Xf/bbtsTtftM2rYbip1KSFFgqncevg/gxGjzpmj9cu2Dss51OyaotnmNAzneVzPKfsHQ+BzhYvOHc5ClGABxnzIz5bNSRrPOMHabf8iTp0eivmr9H6dDCBIhQD+VbJjU9lmWCJr+DEL8yBs0awt0Vqoo6U2c= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com; spf=pass smtp.mailfrom=huawei.com; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b=jptfoX3j; arc=none smtp.client-ip=113.46.200.221 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=huawei.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=huawei.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=huawei.com header.i=@huawei.com header.b="jptfoX3j" dkim-signature: v=1; a=rsa-sha256; d=huawei.com; s=dkim; c=relaxed/relaxed; q=dns/txt; h=From; bh=lruRs8CzfrD5whlMWEhCk2Jf8Ub5XicT0g8tNVBeR0g=; b=jptfoX3j3NMXGp+gPq/kfJAxA7y6KPjhzTqYZ/3O4landdjN6NVrm98q6tMEd2zSTFDkotQew vg2utcWtOn0RKuhY75Cl8n+UsTl39/Gkk+mzagxYLc2czUlu2OM9Z7yXGYkPI8RzLIrwWGjZbpj s4kuw8yPV+lOAKtWKFn+1eU= Received: from mail.maildlp.com (unknown [172.19.162.140]) by canpmsgout06.his.huawei.com (SkyGuard) with ESMTPS id 4gnMpT3n2KzRhQg; Sat, 27 Jun 2026 14:20:01 +0800 (CST) Received: from dggemv712-chm.china.huawei.com (unknown [10.1.198.32]) by mail.maildlp.com (Postfix) with ESMTPS id 60015203B7; Sat, 27 Jun 2026 14:29:07 +0800 (CST) Received: from kwepemq200017.china.huawei.com (7.202.195.228) by dggemv712-chm.china.huawei.com (10.1.198.32) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.11; Sat, 27 Jun 2026 14:29:07 +0800 Received: from octopus.huawei.com (10.67.174.191) by kwepemq200017.china.huawei.com (7.202.195.228) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.11; Sat, 27 Jun 2026 14:29:06 +0800 From: Cai Xinchen To: , , , , , , , , , , , , CC: , , , , , , , Subject: [PATCH v2 stable/linux-6.6.y 2/3] lsm: add backing_file LSM hooks Date: Sat, 27 Jun 2026 14:57:19 +0800 Message-ID: <20260627065720.1945589-3-caixinchen1@huawei.com> X-Mailer: git-send-email 2.18.0.huawei.25 In-Reply-To: <20260627065720.1945589-1-caixinchen1@huawei.com> References: <20260627065720.1945589-1-caixinchen1@huawei.com> Precedence: bulk X-Mailing-List: linux-security-module@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain X-ClientProxiedBy: kwepems500002.china.huawei.com (7.221.188.17) To kwepemq200017.china.huawei.com (7.202.195.228) From: Paul Moore [ 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 Reviewed-by: Serge Hallyn Reviewed-by: Christian Brauner Signed-off-by: Paul Moore [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 --- fs/file_table.c | 32 +++++++--- fs/internal.h | 3 +- fs/open.c | 7 ++- fs/overlayfs/file.c | 8 ++- 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, 190 insertions(+), 15 deletions(-) diff --git a/fs/file_table.c b/fs/file_table.c index b4c208a77153..fa4d4c54f790 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -48,12 +48,12 @@ static struct percpu_counter nr_files __cacheline_aligned_in_smp; struct backing_file { struct file file; struct path real_path; +#ifdef CONFIG_SECURITY + void *security; +#endif }; -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_real_path(struct file *f) { @@ -72,8 +72,21 @@ static void file_free_rcu(struct rcu_head *head) kmem_cache_free(filp_cachep, 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->real_path); } @@ -257,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->real_path, 0, sizeof(ff->real_path)); - return 0; + backing_file_set_security(&ff->file, NULL); + return security_backing_file_alloc(&ff->file, user_file); } /* @@ -270,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; @@ -287,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 d64ae03998cc..576026e1ce3a 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -93,7 +93,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); static inline void put_file_access(struct file *file) { diff --git a/fs/open.c b/fs/open.c index b5ea1dcbfb22..5b164481d80b 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1175,18 +1175,19 @@ EXPORT_SYMBOL_GPL(kernel_file_open); * 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, +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; - f->f_path = *path; + f->f_path = *user_path; path_get(real_path); *backing_file_real_path(f) = *real_path; error = do_dentry_open(f, d_inode(real_path->dentry), NULL); diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 8be4dc050d1e..07f00cf91977 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -60,7 +60,7 @@ static struct file *ovl_open_realfile(const struct file *file, if (!inode_owner_or_capable(real_idmap, realinode)) flags &= ~O_NOATIME; - realfile = backing_file_open(&file->f_path, flags, realpath, + realfile = backing_file_open(file, flags, realpath, current_cred()); } revert_creds(old_cred); @@ -527,6 +527,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; @@ -540,6 +541,11 @@ static int ovl_mmap(struct file *file, struct vm_area_struct *vma) vma_set_file(vma, 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); + return ret; + } ret = call_mmap(vma->vm_file, vma); revert_creds(old_cred); ovl_file_accessed(file); diff --git a/include/linux/fs.h b/include/linux/fs.h index 4cdeeaedaa40..5a897ee50f29 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2515,11 +2515,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 *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_real_path(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_real_path - get the path corresponding to f_inode * diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index 97a8b21eb033..c0a2839253fa 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -93,7 +93,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 2923754c13bc..567d7f16609c 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -169,6 +169,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, @@ -176,6 +179,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 dcb5e5b5eb13..3b56b60195ce 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -59,6 +59,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 937840870d86..4866ffdb4e6c 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -389,11 +389,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); @@ -984,6 +990,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) { @@ -1003,6 +1018,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 1794860fd614..4b61766c3d27 100644 --- a/security/security.c +++ b/security/security.c @@ -78,6 +78,7 @@ struct security_hook_heads security_hook_heads __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; @@ -200,6 +201,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. @@ -374,6 +377,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); @@ -388,6 +392,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, @@ -616,6 +625,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 @@ -2630,6 +2663,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); + } +} + /** * security_file_ioctl() - Check if an ioctl is allowed * @file: associated file @@ -2723,6 +2807,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); + /** * security_mmap_addr() - Check if mmap'ing an address is allowed * @addr: address -- 2.18.0.huawei.25