Commit 19e1dbdc authored by Al Viro's avatar Al Viro
Browse files

fuse_dentry_revalidate(): use stable parent inode and name passed by caller



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: default avatarJeff Layton <jlayton@kernel.org>
Acked-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 46436113
Loading
Loading
Loading
Loading
+7 −10
Original line number Diff line number Diff line
@@ -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;