diff mbox series

[3/9] anon_inode: explicitly block ->setattr()

Message ID 20250407-work-anon_inode-v1-3-53a44c20d44e@kernel.org (mailing list archive)
State New
Headers show
Series fs: harden anon inodes | expand

Commit Message

Christian Brauner April 7, 2025, 9:54 a.m. UTC
It is currently possible to change the mode and owner of the single
anonymous inode in the kernel:

int main(int argc, char *argv[])
{
        int ret, sfd;
        sigset_t mask;
        struct signalfd_siginfo fdsi;

        sigemptyset(&mask);
        sigaddset(&mask, SIGINT);
        sigaddset(&mask, SIGQUIT);

        ret = sigprocmask(SIG_BLOCK, &mask, NULL);
        if (ret < 0)
                _exit(1);

        sfd = signalfd(-1, &mask, 0);
        if (sfd < 0)
                _exit(2);

        ret = fchown(sfd, 5555, 5555);
        if (ret < 0)
                _exit(3);

        ret = fchmod(sfd, 0777);
        if (ret < 0)
                _exit(3);

        _exit(4);
}

This is a bug. It's not really a meaningful one because anonymous inodes
don't really figure into path lookup and they cannot be reopened via
/proc/<pid>/fd/<nr> and can't be used for lookup itself. So they can
only ever serve as direct references.

But it is still completely bogus to allow the mode and ownership or any
of the properties of the anonymous inode to be changed. Block this!

Cc: <stable@vger.kernel.org> # all LTS kernels
Signed-off-by: Christian Brauner <brauner@kernel.org>
---
 fs/anon_inodes.c | 7 +++++++
 fs/internal.h    | 2 ++
 2 files changed, 9 insertions(+)

Comments

Jan Kara April 7, 2025, 2:05 p.m. UTC | #1
On Mon 07-04-25 11:54:17, Christian Brauner wrote:
> It is currently possible to change the mode and owner of the single
> anonymous inode in the kernel:
> 
> int main(int argc, char *argv[])
> {
>         int ret, sfd;
>         sigset_t mask;
>         struct signalfd_siginfo fdsi;
> 
>         sigemptyset(&mask);
>         sigaddset(&mask, SIGINT);
>         sigaddset(&mask, SIGQUIT);
> 
>         ret = sigprocmask(SIG_BLOCK, &mask, NULL);
>         if (ret < 0)
>                 _exit(1);
> 
>         sfd = signalfd(-1, &mask, 0);
>         if (sfd < 0)
>                 _exit(2);
> 
>         ret = fchown(sfd, 5555, 5555);
>         if (ret < 0)
>                 _exit(3);
> 
>         ret = fchmod(sfd, 0777);
>         if (ret < 0)
>                 _exit(3);
> 
>         _exit(4);
> }
> 
> This is a bug. It's not really a meaningful one because anonymous inodes
> don't really figure into path lookup and they cannot be reopened via
> /proc/<pid>/fd/<nr> and can't be used for lookup itself. So they can
> only ever serve as direct references.
> 
> But it is still completely bogus to allow the mode and ownership or any
> of the properties of the anonymous inode to be changed. Block this!
> 
> Cc: <stable@vger.kernel.org> # all LTS kernels
> Signed-off-by: Christian Brauner <brauner@kernel.org>

Definitely. Feel free to add:

Reviewed-by: Jan Kara <jack@suse.cz>

								Honza

> ---
>  fs/anon_inodes.c | 7 +++++++
>  fs/internal.h    | 2 ++
>  2 files changed, 9 insertions(+)
> 
> diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c
> index 42e4b9c34f89..cb51a90bece0 100644
> --- a/fs/anon_inodes.c
> +++ b/fs/anon_inodes.c
> @@ -57,8 +57,15 @@ int anon_inode_getattr(struct mnt_idmap *idmap, const struct path *path,
>  	return 0;
>  }
>  
> +int anon_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> +		       struct iattr *attr)
> +{
> +	return -EOPNOTSUPP;
> +}
> +
>  static const struct inode_operations anon_inode_operations = {
>  	.getattr = anon_inode_getattr,
> +	.setattr = anon_inode_setattr,
>  };
>  
>  /*
> diff --git a/fs/internal.h b/fs/internal.h
> index 717dc9eb6185..f545400ce607 100644
> --- a/fs/internal.h
> +++ b/fs/internal.h
> @@ -346,3 +346,5 @@ int statmount_mnt_idmap(struct mnt_idmap *idmap, struct seq_file *seq, bool uid_
>  int anon_inode_getattr(struct mnt_idmap *idmap, const struct path *path,
>  		       struct kstat *stat, u32 request_mask,
>  		       unsigned int query_flags);
> +int anon_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> +		       struct iattr *attr);
> 
> -- 
> 2.47.2
>
diff mbox series

Patch

diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c
index 42e4b9c34f89..cb51a90bece0 100644
--- a/fs/anon_inodes.c
+++ b/fs/anon_inodes.c
@@ -57,8 +57,15 @@  int anon_inode_getattr(struct mnt_idmap *idmap, const struct path *path,
 	return 0;
 }
 
+int anon_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+		       struct iattr *attr)
+{
+	return -EOPNOTSUPP;
+}
+
 static const struct inode_operations anon_inode_operations = {
 	.getattr = anon_inode_getattr,
+	.setattr = anon_inode_setattr,
 };
 
 /*
diff --git a/fs/internal.h b/fs/internal.h
index 717dc9eb6185..f545400ce607 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -346,3 +346,5 @@  int statmount_mnt_idmap(struct mnt_idmap *idmap, struct seq_file *seq, bool uid_
 int anon_inode_getattr(struct mnt_idmap *idmap, const struct path *path,
 		       struct kstat *stat, u32 request_mask,
 		       unsigned int query_flags);
+int anon_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
+		       struct iattr *attr);