On Tue, Jan 18, 2022 at 2:00 PM Amir Goldstein amir73il@gmail.com wrote:
Apparently, there are some applications that use IN_DELETE event as an invalidation mechanism and expect that if they try to open a file with the name reported with the delete event, that it should not contain the content of the deleted file.
Commit 49246466a989 ("fsnotify: move fsnotify_nameremove() hook out of d_delete()") moved the fsnotify delete hook before d_delete() so fsnotify will have access to a positive dentry.
This allowed a race where opening the deleted file via cached dentry is now possible after receiving the IN_DELETE event.
To fix the regression, we use two different techniques:
- For call sites that call d_delete() with elevated refcount, convert the call to d_drop() and move the fsnotify hook after d_drop().
- For the vfs helpers that may turn dentry to negative on d_delete(), use a helper d_delete_notify() to pin the inode, so we can pass it to an fsnotify hook after d_delete().
Create a new hook fsnotify_delete() that allows to pass a negative dentry and takes the unlinked inode as an argument.
Add a missing fsnotify_unlink() hook in nfsdfs that was found during the call sites audit.
Note that the call sites in simple_recursive_removal() follow d_invalidate(), so they require no change.
Backporting hint: this regression is from v5.3. Although patch will apply with only trivial conflicts to v5.4 and v5.10, it won't build, because fsnotify_delete() implementation is different in each of those versions (see fsnotify_link()).
Reported-by: Ivan Delalande colona@arista.com Link: https://lore.kernel.org/linux-fsdevel/YeNyzoDM5hP5LtGW@visor/ Fixes: 49246466a989 ("fsnotify: move fsnotify_nameremove() hook out of d_delete()") Cc: stable@vger.kernel.org # v5.3+ Signed-off-by: Amir Goldstein amir73il@gmail.com
Jan,
This turned into an audit of fsnotify_unlink/rmdir() call sites, so besides fixing the regression, I also added one missing hook and replaced most of the d_delete() calls with d_drop() to simplify things.
I will follow up with backports for v5.4 and v5.10 and will send the repro to LTP guys.
Thanks, Amir.
fs/btrfs/ioctl.c | 5 ++--- fs/configfs/dir.c | 6 +++--- fs/devpts/inode.c | 2 +- fs/namei.c | 27 ++++++++++++++++++++++----- fs/nfsd/nfsctl.c | 5 +++-- include/linux/fsnotify.h | 20 ++++++++++++++++++++ net/sunrpc/rpc_pipe.c | 4 ++-- 7 files changed, 53 insertions(+), 16 deletions(-)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index edfecfe62b4b..121e8f439996 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -3060,10 +3060,9 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file, btrfs_inode_lock(inode, 0); err = btrfs_delete_subvolume(dir, dentry); btrfs_inode_unlock(inode, 0);
if (!err) {
d_drop(dentry);
if (!err) fsnotify_rmdir(dir, dentry);
d_delete(dentry);
}
oops that an unintentional logic change. Was supposed to be:
if (!err) { + d_drop(dentry); fsnotify_rmdir(dir, dentry); - d_delete(dentry); }
Anyway, fix is pushed to fsnotify-fixes branch.
Thanks, Amir.