Re: [BUG] fanotify: destroy/add race leaves a mark on a detached connector

From: Daehyeon Ko

Date: Fri Aug 28 2026 - 00:24:38 EST


Hi Honza,

You are right that an fd-held dentry cannot satisfy the count == 1 check. I
was imprecise in the report: the fd and the final unlink refer to two
different hard-link dentries for the same inode.

The setup is:

1. create A/x and link it as B/y;
2. open B/y, add the old-group inode mark through that fd, then unlink
B/y, leaving i_nlink == 1 while the fd retains the B/y dentry;
3. race these two calls:

unlink("A/x")

fanotify_mark(racer_group, FAN_MARK_ADD, FAN_MODIFY,
victim_fd, NULL)

where victim_fd still refers to the already unlinked B/y dentry.

So the A/x dentry checked by d_delete() is not held by the fd and can have
d_lockref.count == 1. Its unlink drops the inode's last link and reaches
dentry_unlink_inode() -> fsnotify_inoderemove(). At the same time,
fanotify_find_path() gets the same inode through the fd-held B/y dentry and
adds the racer mark.

Your suspicion matches this topology: __fsnotify_inode_delete() from
fsnotify_inoderemove() is running while the inode is still live and reachable
through the other, fd-held dentry. Deferring mark cleanup until
__destroy_inode() would avoid this particular add-versus-cleanup race. I have
not audited the broader consequences of that change yet.

I can send the exact reproducer privately if that would be useful.

Thanks,
Daehyeon