| From d33f89b34aa313f50f9a512d58dd288999f246b0 Mon Sep 17 00:00:00 2001 |
| From: Deepanshu Kartikey <kartikey406@gmail.com> |
| Date: Mon, 27 Oct 2025 18:36:34 +0530 |
| Subject: f2fs: invalidate dentry cache on failed whiteout creation |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: Deepanshu Kartikey <kartikey406@gmail.com> |
| |
| commit d33f89b34aa313f50f9a512d58dd288999f246b0 upstream. |
| |
| F2FS can mount filesystems with corrupted directory depth values that |
| get runtime-clamped to MAX_DIR_HASH_DEPTH. When RENAME_WHITEOUT |
| operations are performed on such directories, f2fs_rename performs |
| directory modifications (updating target entry and deleting source |
| entry) before attempting to add the whiteout entry via f2fs_add_link. |
| |
| If f2fs_add_link fails due to the corrupted directory structure, the |
| function returns an error to VFS, but the partial directory |
| modifications have already been committed to disk. VFS assumes the |
| entire rename operation failed and does not update the dentry cache, |
| leaving stale mappings. |
| |
| In the error path, VFS does not call d_move() to update the dentry |
| cache. This results in new_dentry still pointing to the old inode |
| (new_inode) which has already had its i_nlink decremented to zero. |
| The stale cache causes subsequent operations to incorrectly reference |
| the freed inode. |
| |
| This causes subsequent operations to use cached dentry information that |
| no longer matches the on-disk state. When a second rename targets the |
| same entry, VFS attempts to decrement i_nlink on the stale inode, which |
| may already have i_nlink=0, triggering a WARNING in drop_nlink(). |
| |
| Example sequence: |
| 1. First rename (RENAME_WHITEOUT): file2 → file1 |
| - f2fs updates file1 entry on disk (points to inode 8) |
| - f2fs deletes file2 entry on disk |
| - f2fs_add_link(whiteout) fails (corrupted directory) |
| - Returns error to VFS |
| - VFS does not call d_move() due to error |
| - VFS cache still has: file1 → inode 7 (stale!) |
| - inode 7 has i_nlink=0 (already decremented) |
| |
| 2. Second rename: file3 → file1 |
| - VFS uses stale cache: file1 → inode 7 |
| - Tries to drop_nlink on inode 7 (i_nlink already 0) |
| - WARNING in drop_nlink() |
| |
| Fix this by explicitly invalidating old_dentry and new_dentry when |
| f2fs_add_link fails during whiteout creation. This forces VFS to |
| refresh from disk on subsequent operations, ensuring cache consistency |
| even when the rename partially succeeds. |
| |
| Reproducer: |
| 1. Mount F2FS image with corrupted i_current_depth |
| 2. renameat2(file2, file1, RENAME_WHITEOUT) |
| 3. renameat2(file3, file1, 0) |
| 4. System triggers WARNING in drop_nlink() |
| |
| Fixes: 7e01e7ad746b ("f2fs: support RENAME_WHITEOUT") |
| Reported-by: syzbot+632cf32276a9a564188d@syzkaller.appspotmail.com |
| Closes: https://syzkaller.appspot.com/bug?extid=632cf32276a9a564188d |
| Suggested-by: Chao Yu <chao@kernel.org> |
| Link: https://lore.kernel.org/all/20251022233349.102728-1-kartikey406@gmail.com/ [v1] |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com> |
| Reviewed-by: Chao Yu <chao@kernel.org> |
| Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/f2fs/namei.c | 6 ++++-- |
| 1 file changed, 4 insertions(+), 2 deletions(-) |
| |
| --- a/fs/f2fs/namei.c |
| +++ b/fs/f2fs/namei.c |
| @@ -1028,9 +1028,11 @@ static int f2fs_rename(struct user_names |
| if (whiteout) { |
| set_inode_flag(whiteout, FI_INC_LINK); |
| err = f2fs_add_link(old_dentry, whiteout); |
| - if (err) |
| + if (err) { |
| + d_invalidate(old_dentry); |
| + d_invalidate(new_dentry); |
| goto put_out_dir; |
| - |
| + } |
| spin_lock(&whiteout->i_lock); |
| whiteout->i_state &= ~I_LINKABLE; |
| spin_unlock(&whiteout->i_lock); |