]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ext4: prevent stale extent cache entries caused by concurrent get es_cache
authorZhang Yi <yi.zhang@huawei.com>
Wed, 23 Apr 2025 08:52:53 +0000 (16:52 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 27 Jun 2025 10:05:30 +0000 (11:05 +0100)
[ Upstream commit f22a0ef2231a7d8374bb021eb86404d0e9de5a02 ]

The EXT4_IOC_GET_ES_CACHE and EXT4_IOC_PRECACHE_EXTENTS currently
invokes ext4_ext_precache() to preload the extent cache without holding
the inode's i_rwsem. This can result in stale extent cache entries when
competing with operations such as ext4_collapse_range() which calls
ext4_ext_remove_space() or ext4_ext_shift_extents().

The problem arises when ext4_ext_remove_space() temporarily releases
i_data_sem due to insufficient journal credits. During this interval, a
concurrent EXT4_IOC_GET_ES_CACHE or EXT4_IOC_PRECACHE_EXTENTS may cache
extent entries that are about to be deleted. As a result, these cached
entries become stale and inconsistent with the actual extents.

Loading the extents cache without holding the inode's i_rwsem or the
mapping's invalidate_lock is not permitted besides during the writeback.
Fix this by holding the i_rwsem during EXT4_IOC_GET_ES_CACHE and
EXT4_IOC_PRECACHE_EXTENTS.

Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Link: https://patch.msgid.link/20250423085257.122685-6-yi.zhang@huaweicloud.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/ext4/extents.c
fs/ext4/ioctl.c

index e41a6d642472bcc039420da9737bb0d0ee20fe54..35bc58a26f7f4e11ec76d845a00149516e538a1c 100644 (file)
@@ -5041,7 +5041,9 @@ int ext4_get_es_cache(struct inode *inode, struct fiemap_extent_info *fieinfo,
        }
 
        if (fieinfo->fi_flags & FIEMAP_FLAG_CACHE) {
+               inode_lock_shared(inode);
                error = ext4_ext_precache(inode);
+               inode_unlock_shared(inode);
                if (error)
                        return error;
                fieinfo->fi_flags &= ~FIEMAP_FLAG_CACHE;
index 18002b0a908ce726ffd67528f8f320c6eb2d2312..bd90b454c62136b796a021af2667ace148f81114 100644 (file)
@@ -1130,8 +1130,14 @@ resizefs_out:
                return 0;
        }
        case EXT4_IOC_PRECACHE_EXTENTS:
-               return ext4_ext_precache(inode);
+       {
+               int ret;
 
+               inode_lock_shared(inode);
+               ret = ext4_ext_precache(inode);
+               inode_unlock_shared(inode);
+               return ret;
+       }
        case FS_IOC_SET_ENCRYPTION_POLICY:
                if (!ext4_has_feature_encrypt(sb))
                        return -EOPNOTSUPP;