]> www.infradead.org Git - users/willy/pagecache.git/commitdiff
fuse_dentry_revalidate(): use stable parent inode and name passed by caller
authorAl Viro <viro@zeniv.linux.org.uk>
Fri, 3 Jan 2025 06:20:35 +0000 (01:20 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Tue, 28 Jan 2025 00:25:24 +0000 (19:25 -0500)
No need to mess with dget_parent() for the former; for the latter we really should
not rely upon ->d_name.name remaining stable - it's a real-life UAF.

Reviewed-by: Jeff Layton <jlayton@kernel.org>
Acked-by: Miklos Szeredi <mszeredi@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/fuse/dir.c

index d9e9f26917eb81b3bc55e88be450ab70a58a8852..3019bc1d9f9de4018b078963a6cfccfb61dd9308 100644 (file)
@@ -175,9 +175,11 @@ static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args,
        memset(outarg, 0, sizeof(struct fuse_entry_out));
        args->opcode = FUSE_LOOKUP;
        args->nodeid = nodeid;
-       args->in_numargs = 1;
-       args->in_args[0].size = name->len + 1;
+       args->in_numargs = 2;
+       args->in_args[0].size = name->len;
        args->in_args[0].value = name->name;
+       args->in_args[1].size = 1;
+       args->in_args[1].value = "";
        args->out_numargs = 1;
        args->out_args[0].size = sizeof(struct fuse_entry_out);
        args->out_args[0].value = outarg;
@@ -196,7 +198,6 @@ static int fuse_dentry_revalidate(struct inode *dir, const struct qstr *name,
                                  struct dentry *entry, unsigned int flags)
 {
        struct inode *inode;
-       struct dentry *parent;
        struct fuse_mount *fm;
        struct fuse_inode *fi;
        int ret;
@@ -228,11 +229,9 @@ static int fuse_dentry_revalidate(struct inode *dir, const struct qstr *name,
 
                attr_version = fuse_get_attr_version(fm->fc);
 
-               parent = dget_parent(entry);
-               fuse_lookup_init(fm->fc, &args, get_node_id(d_inode(parent)),
-                                &entry->d_name, &outarg);
+               fuse_lookup_init(fm->fc, &args, get_node_id(dir),
+                                name, &outarg);
                ret = fuse_simple_request(fm, &args);
-               dput(parent);
                /* Zero nodeid is same as -ENOENT */
                if (!ret && !outarg.nodeid)
                        ret = -ENOENT;
@@ -266,9 +265,7 @@ static int fuse_dentry_revalidate(struct inode *dir, const struct qstr *name,
                        if (test_bit(FUSE_I_INIT_RDPLUS, &fi->state))
                                return -ECHILD;
                } else if (test_and_clear_bit(FUSE_I_INIT_RDPLUS, &fi->state)) {
-                       parent = dget_parent(entry);
-                       fuse_advise_use_readdirplus(d_inode(parent));
-                       dput(parent);
+                       fuse_advise_use_readdirplus(dir);
                }
        }
        ret = 1;