Skip to content

Commit 6358461

Browse files
committed
Merge patch series "fuse: fixes and cleanups for expired dentry eviction"
Miklos Szeredi <mszeredi@redhat.com> says: This mini series fixes issues with the stale dentry cleanup patches added in this cycle. In particular commit ab84ad5 ("fuse: new work queue to periodically invalidate expired dentries") allowed a race resulting in UAF. * patches from https://patch.msgid.link/20260114145344.468856-1-mszeredi@redhat.com: vfs: document d_dispose_if_unused() fuse: shrink once after all buckets have been scanned fuse: clean up fuse_dentry_tree_work() fuse: add need_resched() before unlocking bucket fuse: make sure dentry is evicted if stale fuse: fix race when disposing stale dentries Link: https://patch.msgid.link/20260114145344.468856-1-mszeredi@redhat.com Signed-off-by: Christian Brauner <brauner@kernel.org>
2 parents 4973d95 + 79d1131 commit 6358461

File tree

2 files changed

+24
-15
lines changed

2 files changed

+24
-15
lines changed

fs/dcache.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,16 @@ struct dentry *d_find_alias_rcu(struct inode *inode)
11041104
return de;
11051105
}
11061106

1107+
/**
1108+
* d_dispose_if_unused - move unreferenced dentries to shrink list
1109+
* @dentry: dentry in question
1110+
* @dispose: head of shrink list
1111+
*
1112+
* If dentry has no external references, move it to shrink list.
1113+
*
1114+
* NOTE!!! The caller is responsible for preventing eviction of the dentry by
1115+
* holding dentry->d_inode->i_lock or equivalent.
1116+
*/
11071117
void d_dispose_if_unused(struct dentry *dentry, struct list_head *dispose)
11081118
{
11091119
spin_lock(&dentry->d_lock);

fs/fuse/dir.c

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -169,20 +169,26 @@ static void fuse_dentry_tree_work(struct work_struct *work)
169169
node = rb_first(&dentry_hash[i].tree);
170170
while (node) {
171171
fd = rb_entry(node, struct fuse_dentry, node);
172-
if (time_after64(get_jiffies_64(), fd->time)) {
173-
rb_erase(&fd->node, &dentry_hash[i].tree);
174-
RB_CLEAR_NODE(&fd->node);
172+
if (!time_before64(fd->time, get_jiffies_64()))
173+
break;
174+
175+
rb_erase(&fd->node, &dentry_hash[i].tree);
176+
RB_CLEAR_NODE(&fd->node);
177+
spin_lock(&fd->dentry->d_lock);
178+
/* If dentry is still referenced, let next dput release it */
179+
fd->dentry->d_flags |= DCACHE_OP_DELETE;
180+
spin_unlock(&fd->dentry->d_lock);
181+
d_dispose_if_unused(fd->dentry, &dispose);
182+
if (need_resched()) {
175183
spin_unlock(&dentry_hash[i].lock);
176-
d_dispose_if_unused(fd->dentry, &dispose);
177184
cond_resched();
178185
spin_lock(&dentry_hash[i].lock);
179-
} else
180-
break;
186+
}
181187
node = rb_first(&dentry_hash[i].tree);
182188
}
183189
spin_unlock(&dentry_hash[i].lock);
184-
shrink_dentry_list(&dispose);
185190
}
191+
shrink_dentry_list(&dispose);
186192

187193
if (inval_wq)
188194
schedule_delayed_work(&dentry_tree_work,
@@ -479,18 +485,12 @@ static int fuse_dentry_init(struct dentry *dentry)
479485
return 0;
480486
}
481487

482-
static void fuse_dentry_prune(struct dentry *dentry)
488+
static void fuse_dentry_release(struct dentry *dentry)
483489
{
484490
struct fuse_dentry *fd = dentry->d_fsdata;
485491

486492
if (!RB_EMPTY_NODE(&fd->node))
487493
fuse_dentry_tree_del_node(dentry);
488-
}
489-
490-
static void fuse_dentry_release(struct dentry *dentry)
491-
{
492-
struct fuse_dentry *fd = dentry->d_fsdata;
493-
494494
kfree_rcu(fd, rcu);
495495
}
496496

@@ -527,7 +527,6 @@ const struct dentry_operations fuse_dentry_operations = {
527527
.d_revalidate = fuse_dentry_revalidate,
528528
.d_delete = fuse_dentry_delete,
529529
.d_init = fuse_dentry_init,
530-
.d_prune = fuse_dentry_prune,
531530
.d_release = fuse_dentry_release,
532531
.d_automount = fuse_dentry_automount,
533532
};

0 commit comments

Comments
 (0)