Re: [BUG] fanotify: destroy/add race leaves a mark on a detached connector
From: Jan Kara
Date: Mon Oct 19 2026 - 04:46:01 EST
On Mon 12-10-26 15:26:59, Amir Goldstein wrote:
> From ff91e13847a5dbee4e864a47be5bf4bcc8199b74 Mon Sep 17 00:00:00 2001
> From: Amir Goldstein <amir73il@xxxxxxxxx>
> Date: Wed, 2 Sep 2026 18:49:25 +0200
> Subject: [PATCH v3] fs: make sure to call fsnotify_inoderemove() only once
> when inode is dead
>
> Daehyeon reported a race wherein fsnotify_removeinode() can be called
> when inode still has a live alias. This can lead to several undesired
> outcomes such as crashes in fsnotify code or avoidance of inode mark
> notifications.
>
> Check that inode is really "dead", meaning no nlink and no more aliases
> before calling fsnotify_inoderemove() for final cleanup of inode marks.
>
> Ideally, we'd call fsnotify_inoderemove() once last inode reference is
> dropped to make things simpler. Alas fsnotify inode marks currently hold
> inode references and thus this doesn't work. Once we teach fsnotify to
> avoid holding inode references, these games with dead inodes can go as
> well.
>
> Note that a new alias could be created by open_by_handle() to an inode
> that has already reported FS_DELETE_SELF. This case will be handled by
> a different patch.
>
> Reported-by: Daehyeon Ko <4ncienth@xxxxxxxxx>
> Closes: https://lore.kernel.org/linux-fsdevel/20260827045007.3831259-1-4ncienth@xxxxxxxxx/
> CC: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx>
> Link: https://patch.msgid.link/20260902164924.1333135-2-jack@xxxxxxx
This got burried in my inbox. Feel free to add:
Reviewed-by: Jan Kara <jack@xxxxxxx>
Honza
> ---
> fs/dcache.c | 14 +++++++++++++-
> 1 file changed, 13 insertions(+), 1 deletion(-)
>
> diff --git a/fs/dcache.c b/fs/dcache.c
> index b89c8480b655b..86a3a00929de1 100644
> --- a/fs/dcache.c
> +++ b/fs/dcache.c
> @@ -450,6 +450,16 @@ static void dentry_free(struct dentry *dentry)
> call_rcu(&dentry->d_rcu, __d_free);
> }
>
> +/*
> + * When inode is unlinked and doesn't have any aliases (i.e., all fds pointing
> + * to it are closed) we report FS_DELETE_SELF to fsnotify and cleanup all inode
> + * marks.
> + */
> +static bool inode_notify_dead(struct inode *inode)
> +{
> + return !inode->i_nlink && hlist_empty(&inode->i_dentry);
> +}
> +
> /*
> * Release the dentry's inode, using the filesystem
> * d_iput() operation if defined.
> @@ -459,6 +469,7 @@ static void dentry_unlink_inode(struct dentry * dentry)
> __releases(dentry->d_inode->i_lock)
> {
> struct inode *inode = dentry->d_inode;
> + bool notify_dead;
>
> raw_write_seqcount_begin(&dentry->d_seq);
> __d_clear_type_and_inode(dentry);
> @@ -469,9 +480,10 @@ static void dentry_unlink_inode(struct dentry * dentry)
> */
> dentry->waiters = NULL;
> raw_write_seqcount_end(&dentry->d_seq);
> + notify_dead = inode_notify_dead(inode);
> spin_unlock(&dentry->d_lock);
> spin_unlock(&inode->i_lock);
> - if (!inode->i_nlink)
> + if (notify_dead)
> fsnotify_inoderemove(inode);
> if (dentry->d_op && dentry->d_op->d_iput)
> dentry->d_op->d_iput(dentry, inode);
> --
> 2.55.0
>
--
Jan Kara <jack@xxxxxxxx>
SUSE Labs, CR