On Fri, Jun 20, 2025 at 07:03:30AM +0000, Shivank Garg wrote: > Export anon_inode_make_secure_inode() to allow KVM guest_memfd to create > anonymous inodes with proper security context. This replaces the current > pattern of calling alloc_anon_inode() followed by > inode_init_security_anon() for creating security context manually. > > This change also fixes a security regression in secretmem where the > S_PRIVATE flag was not cleared after alloc_anon_inode(), causing > LSM/SELinux checks to be bypassed for secretmem file descriptors. > > As guest_memfd currently resides in the KVM module, we need to export this > symbol for use outside the core kernel. In the future, guest_memfd might be > moved to core-mm, at which point the symbols no longer would have to be > exported. When/if that happens is still unclear. > > Fixes: 2bfe15c52612 ("mm: create security context for memfd_secret inodes") > Suggested-by: David Hildenbrand <david@xxxxxxxxxx> > Suggested-by: Mike Rapoport <rppt@xxxxxxxxxx> > Signed-off-by: Shivank Garg <shivankg@xxxxxxx> Acked-by: Mike Rapoport (Microsoft) <rppt@xxxxxxxxxx> > --- > The handling of the S_PRIVATE flag for these inodes was discussed > extensively ([1], [2], [3]). > > As per discussion [3] with Mike and Paul, KVM guest_memfd and secretmem > result in user-visible file descriptors, so they should be subject to > LSM/SELinux security policies rather than bypassing them with S_PRIVATE. > > [1] https://lore.kernel.org/all/b9e5fa41-62fd-4b3d-bb2d-24ae9d3c33da@xxxxxxxxxx > [2] https://lore.kernel.org/all/cover.1748890962.git.ackerleytng@xxxxxxxxxx > [3] https://lore.kernel.org/all/aFOh8N_rRdSi_Fbc@xxxxxxxxxx > > V1->V2: Use EXPORT_SYMBOL_GPL_FOR_MODULES() since KVM is the only user. > > fs/anon_inodes.c | 23 ++++++++++++++++++----- > include/linux/fs.h | 2 ++ > mm/secretmem.c | 9 +-------- > 3 files changed, 21 insertions(+), 13 deletions(-) > > diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c > index e51e7d88980a..1d847a939f29 100644 > --- a/fs/anon_inodes.c > +++ b/fs/anon_inodes.c > @@ -98,14 +98,25 @@ static struct file_system_type anon_inode_fs_type = { > .kill_sb = kill_anon_super, > }; > > -static struct inode *anon_inode_make_secure_inode( > - const char *name, > - const struct inode *context_inode) > +/** > + * anon_inode_make_secure_inode - allocate an anonymous inode with security context > + * @sb: [in] Superblock to allocate from > + * @name: [in] Name of the class of the newfile (e.g., "secretmem") > + * @context_inode: > + * [in] Optional parent inode for security inheritance > + * > + * The function ensures proper security initialization through the LSM hook > + * security_inode_init_security_anon(). > + * > + * Return: Pointer to new inode on success, ERR_PTR on failure. > + */ > +struct inode *anon_inode_make_secure_inode(struct super_block *sb, const char *name, > + const struct inode *context_inode) > { > struct inode *inode; > int error; > > - inode = alloc_anon_inode(anon_inode_mnt->mnt_sb); > + inode = alloc_anon_inode(sb); > if (IS_ERR(inode)) > return inode; > inode->i_flags &= ~S_PRIVATE; > @@ -118,6 +129,7 @@ static struct inode *anon_inode_make_secure_inode( > } > return inode; > } > +EXPORT_SYMBOL_GPL_FOR_MODULES(anon_inode_make_secure_inode, "kvm"); > > static struct file *__anon_inode_getfile(const char *name, > const struct file_operations *fops, > @@ -132,7 +144,8 @@ static struct file *__anon_inode_getfile(const char *name, > return ERR_PTR(-ENOENT); > > if (make_inode) { > - inode = anon_inode_make_secure_inode(name, context_inode); > + inode = anon_inode_make_secure_inode(anon_inode_mnt->mnt_sb, > + name, context_inode); > if (IS_ERR(inode)) { > file = ERR_CAST(inode); > goto err; > diff --git a/include/linux/fs.h b/include/linux/fs.h > index b085f161ed22..040c0036320f 100644 > --- a/include/linux/fs.h > +++ b/include/linux/fs.h > @@ -3608,6 +3608,8 @@ extern int simple_write_begin(struct file *file, struct address_space *mapping, > extern const struct address_space_operations ram_aops; > extern int always_delete_dentry(const struct dentry *); > extern struct inode *alloc_anon_inode(struct super_block *); > +struct inode *anon_inode_make_secure_inode(struct super_block *sb, const char *name, > + const struct inode *context_inode); > extern int simple_nosetlease(struct file *, int, struct file_lease **, void **); > extern const struct dentry_operations simple_dentry_operations; > > diff --git a/mm/secretmem.c b/mm/secretmem.c > index 589b26c2d553..9a11a38a6770 100644 > --- a/mm/secretmem.c > +++ b/mm/secretmem.c > @@ -195,18 +195,11 @@ static struct file *secretmem_file_create(unsigned long flags) > struct file *file; > struct inode *inode; > const char *anon_name = "[secretmem]"; > - int err; > > - inode = alloc_anon_inode(secretmem_mnt->mnt_sb); > + inode = anon_inode_make_secure_inode(secretmem_mnt->mnt_sb, anon_name, NULL); > if (IS_ERR(inode)) > return ERR_CAST(inode); > > - err = security_inode_init_security_anon(inode, &QSTR(anon_name), NULL); > - if (err) { > - file = ERR_PTR(err); > - goto err_free_inode; > - } > - > file = alloc_file_pseudo(inode, secretmem_mnt, "secretmem", > O_RDWR, &secretmem_fops); > if (IS_ERR(file)) > -- > 2.43.0 > -- Sincerely yours, Mike.