Re: [PATCH v8 1/3] Add a new LSM-supporting anonymous inode interface

From: Christian Brauner
Date: Tue Sep 01 2020 - 08:42:55 EST


On Wed, Aug 26, 2020 at 11:35:20PM -0700, Lokesh Gidra wrote:
> From: Daniel Colascione <dancol@xxxxxxxxxx>
>
> This change adds a new function, anon_inode_getfd_secure, that creates
> anonymous-node file with individual non-S_PRIVATE inode to which security
> modules can apply policy. Existing callers continue using the original
> singleton-inode kind of anonymous-inode file. We can transition anonymous
> inode users to the new kind of anonymous inode in individual patches for
> the sake of bisection and review.
>
> The new function accepts an optional context_inode parameter that
> callers can use to provide additional contextual information to
> security modules for granting/denying permission to create an anon inode
> of the same type.
>
> For example, in case of userfaultfd, the created inode is a
> 'logical child' of the context_inode (userfaultfd inode of the
> parent process) in the sense that it provides the security context
> required during creation of the child process' userfaultfd inode.
>
> Signed-off-by: Daniel Colascione <dancol@xxxxxxxxxx>
>
> [Fix comment documenting return values of inode_init_security_anon()]
> [Add context_inode description in comments to anon_inode_getfd_secure()]
> [Remove definition of anon_inode_getfile_secure() as there are no callers]
> [Make _anon_inode_getfile() static]
> [Use correct error cast in _anon_inode_getfile()]
> [Fix error handling in _anon_inode_getfile()]
>
> Signed-off-by: Lokesh Gidra <lokeshgidra@xxxxxxxxxx>
> ---
> fs/anon_inodes.c | 147 +++++++++++++++++++++++++---------
> include/linux/anon_inodes.h | 8 ++
> include/linux/lsm_hook_defs.h | 2 +
> include/linux/lsm_hooks.h | 9 +++
> include/linux/security.h | 10 +++
> security/security.c | 8 ++
> 6 files changed, 144 insertions(+), 40 deletions(-)
>
> diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c
> index 89714308c25b..c3f16deda211 100644
> --- a/fs/anon_inodes.c
> +++ b/fs/anon_inodes.c
> @@ -55,61 +55,79 @@ static struct file_system_type anon_inode_fs_type = {
> .kill_sb = kill_anon_super,
> };
>
> -/**
> - * anon_inode_getfile - creates a new file instance by hooking it up to an
> - * anonymous inode, and a dentry that describe the "class"
> - * of the file
> - *
> - * @name: [in] name of the "class" of the new file
> - * @fops: [in] file operations for the new file
> - * @priv: [in] private data for the new file (will be file's private_data)
> - * @flags: [in] flags
> - *
> - * Creates a new file by hooking it on a single inode. This is useful for files
> - * that do not need to have a full-fledged inode in order to operate correctly.
> - * All the files created with anon_inode_getfile() will share a single inode,
> - * hence saving memory and avoiding code duplication for the file/inode/dentry
> - * setup. Returns the newly created file* or an error pointer.
> - */
> -struct file *anon_inode_getfile(const char *name,
> - const struct file_operations *fops,
> - void *priv, int flags)
> +static struct inode *anon_inode_make_secure_inode(
> + const char *name,
> + const struct inode *context_inode)
> {
> - struct file *file;
> + struct inode *inode;
> + const struct qstr qname = QSTR_INIT(name, strlen(name));
> + int error;
> +
> + inode = alloc_anon_inode(anon_inode_mnt->mnt_sb);
> + if (IS_ERR(inode))
> + return inode;
> + inode->i_flags &= ~S_PRIVATE;
> + error = security_inode_init_security_anon(inode, &qname, context_inode);
> + if (error) {
> + iput(inode);
> + return ERR_PTR(error);
> + }
> + return inode;
> +}

Hey,

Iiuc, this makes each newly created anon inode fd correspond to a unique
file and to a unique inode:

fd1 -> file1 -> inode1
fd2 -> file2 -> inode2

Whereas before we had every anon inode fd correspond to a unique file
but all files map to the _same_ inode:

fd1 -> file1 -> inode
fd2 -> file2 -> inode

The old behavior of hooking up a each anon inode fd to the same inode
prevented having an evict method attached to the inode. Because it was
shared that wasn't possible but also simply because that inode never got
evicted anyway. That surely was intended but it's a bummer to some
extent.
With the new model you also can't have an evict method because now you
have a separate inode for each file.

I'm probably going to get killed for suggesting this but:
If we're going to expand the anonymous inode infrastructure anyway is
there a way we can make it so that we have a way to allocate a single
inode for multiple anonymous inode fds and have callers opt-in to this
behavior. We'd need a way to find this inode again, obviously.

This would allow for some features on top of anonymous inode fds that
can refer to the same object, i.e. anonymous inode fds that currently
stash away the same object in f->private_data.
In such a model we could allow such anonymous inode fds to stash away
objects in inode->i_private instead of f->private_data and attach an
evict method to it. This would e.g. allow a process to be killed when
the last pidfd to it is closed or a seccomp notifier fd to notify when
the filter is released without having to do separate reference counting.

This would need a way to lookup that inode by the object that is stashed
away in it of course which could probably be done by an idr or an
xarray or something cleverer. It would obviously only affect a subset of
anonymous inode fds so any other anonymous inode fds wouldn't be
impacted since they can still use the single-anon-inode interface.

Christian