Unverified Commit cb8d2bdc authored by Miklos Szeredi's avatar Miklos Szeredi Committed by Christian Brauner
Browse files

fuse: fix race when disposing stale dentries



In fuse_dentry_tree_work() just before d_dispose_if_unused() the dentry
could get evicted, resulting in UAF.

Move unlocking dentry_hash[i].lock to after the dispose.  To do this,
fuse_dentry_tree_del_node() needs to be moved from fuse_dentry_prune() to
fuse_dentry_release() to prevent an ABBA deadlock.

The lock ordering becomes:

 -> dentry_bucket.lock
    -> dentry.d_lock

Reported-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Closes: https://lore.kernel.org/all/20251206014242.GO1712166@ZenIV/


Fixes: ab84ad59 ("fuse: new work queue to periodically invalidate expired dentries")
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
Link: https://patch.msgid.link/20260114145344.468856-2-mszeredi@redhat.com


Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parent 4973d956
Loading
Loading
Loading
Loading
+2 −9
Original line number Diff line number Diff line
@@ -172,8 +172,8 @@ static void fuse_dentry_tree_work(struct work_struct *work)
			if (time_after64(get_jiffies_64(), fd->time)) {
				rb_erase(&fd->node, &dentry_hash[i].tree);
				RB_CLEAR_NODE(&fd->node);
				spin_unlock(&dentry_hash[i].lock);
				d_dispose_if_unused(fd->dentry, &dispose);
				spin_unlock(&dentry_hash[i].lock);
				cond_resched();
				spin_lock(&dentry_hash[i].lock);
			} else
@@ -479,18 +479,12 @@ static int fuse_dentry_init(struct dentry *dentry)
	return 0;
}

static void fuse_dentry_prune(struct dentry *dentry)
static void fuse_dentry_release(struct dentry *dentry)
{
	struct fuse_dentry *fd = dentry->d_fsdata;

	if (!RB_EMPTY_NODE(&fd->node))
		fuse_dentry_tree_del_node(dentry);
}

static void fuse_dentry_release(struct dentry *dentry)
{
	struct fuse_dentry *fd = dentry->d_fsdata;

	kfree_rcu(fd, rcu);
}

@@ -527,7 +521,6 @@ const struct dentry_operations fuse_dentry_operations = {
	.d_revalidate	= fuse_dentry_revalidate,
	.d_delete	= fuse_dentry_delete,
	.d_init		= fuse_dentry_init,
	.d_prune	= fuse_dentry_prune,
	.d_release	= fuse_dentry_release,
	.d_automount	= fuse_dentry_automount,
};