Skip to content

Commit f83c67b

Browse files
jankarasmb49
authored andcommitted
ext4: fix race when reusing xattr blocks
BugLink: https://bugs.launchpad.net/bugs/2003914 [ Upstream commit 65f8b80 ] When ext4_xattr_block_set() decides to remove xattr block the following race can happen: CPU1 CPU2 ext4_xattr_block_set() ext4_xattr_release_block() new_bh = ext4_xattr_block_cache_find() lock_buffer(bh); ref = le32_to_cpu(BHDR(bh)->h_refcount); if (ref == 1) { ... mb_cache_entry_delete(); unlock_buffer(bh); ext4_free_blocks(); ... ext4_forget(..., bh, ...); jbd2_journal_revoke(..., bh); ext4_journal_get_write_access(..., new_bh, ...) do_get_write_access() jbd2_journal_cancel_revoke(..., new_bh); Later the code in ext4_xattr_block_set() finds out the block got freed and cancels reusal of the block but the revoke stays canceled and so in case of block reuse and journal replay the filesystem can get corrupted. If the race works out slightly differently, we can also hit assertions in the jbd2 code. Fix the problem by making sure that once matching mbcache entry is found, code dropping the last xattr block reference (or trying to modify xattr block in place) waits until the mbcache entry reference is dropped. This way code trying to reuse xattr block is protected from someone trying to drop the last reference to xattr block. Reported-and-tested-by: Ritesh Harjani <ritesh.list@gmail.com> CC: stable@vger.kernel.org Fixes: 82939d7 ("ext4: convert to mbcache2") Signed-off-by: Jan Kara <jack@suse.cz> Link: https://lore.kernel.org/r/20220712105436.32204-5-jack@suse.cz Signed-off-by: Theodore Ts'o <tytso@mit.edu> Stable-dep-of: a44e84a ("ext4: fix deadlock due to mbcache entry corruption") Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Kamal Mostafa <kamal@canonical.com> Signed-off-by: Stefan Bader <stefan.bader@canonical.com>
1 parent 2a7082a commit f83c67b

File tree

1 file changed

+45
-22
lines changed

1 file changed

+45
-22
lines changed

fs/ext4/xattr.c

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -437,9 +437,16 @@ static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino,
437437
/* Remove entry from mbcache when EA inode is getting evicted */
438438
void ext4_evict_ea_inode(struct inode *inode)
439439
{
440-
if (EA_INODE_CACHE(inode))
441-
mb_cache_entry_delete(EA_INODE_CACHE(inode),
442-
ext4_xattr_inode_get_hash(inode), inode->i_ino);
440+
struct mb_cache_entry *oe;
441+
442+
if (!EA_INODE_CACHE(inode))
443+
return;
444+
/* Wait for entry to get unused so that we can remove it */
445+
while ((oe = mb_cache_entry_delete_or_get(EA_INODE_CACHE(inode),
446+
ext4_xattr_inode_get_hash(inode), inode->i_ino))) {
447+
mb_cache_entry_wait_unused(oe);
448+
mb_cache_entry_put(EA_INODE_CACHE(inode), oe);
449+
}
443450
}
444451

445452
static int
@@ -1241,6 +1248,7 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode,
12411248
if (error)
12421249
goto out;
12431250

1251+
retry_ref:
12441252
lock_buffer(bh);
12451253
hash = le32_to_cpu(BHDR(bh)->h_hash);
12461254
ref = le32_to_cpu(BHDR(bh)->h_refcount);
@@ -1250,9 +1258,18 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode,
12501258
* This must happen under buffer lock for
12511259
* ext4_xattr_block_set() to reliably detect freed block
12521260
*/
1253-
if (ea_block_cache)
1254-
mb_cache_entry_delete(ea_block_cache, hash,
1255-
bh->b_blocknr);
1261+
if (ea_block_cache) {
1262+
struct mb_cache_entry *oe;
1263+
1264+
oe = mb_cache_entry_delete_or_get(ea_block_cache, hash,
1265+
bh->b_blocknr);
1266+
if (oe) {
1267+
unlock_buffer(bh);
1268+
mb_cache_entry_wait_unused(oe);
1269+
mb_cache_entry_put(ea_block_cache, oe);
1270+
goto retry_ref;
1271+
}
1272+
}
12561273
get_bh(bh);
12571274
unlock_buffer(bh);
12581275

@@ -1879,9 +1896,20 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
18791896
* ext4_xattr_block_set() to reliably detect modified
18801897
* block
18811898
*/
1882-
if (ea_block_cache)
1883-
mb_cache_entry_delete(ea_block_cache, hash,
1884-
bs->bh->b_blocknr);
1899+
if (ea_block_cache) {
1900+
struct mb_cache_entry *oe;
1901+
1902+
oe = mb_cache_entry_delete_or_get(ea_block_cache,
1903+
hash, bs->bh->b_blocknr);
1904+
if (oe) {
1905+
/*
1906+
* Xattr block is getting reused. Leave
1907+
* it alone.
1908+
*/
1909+
mb_cache_entry_put(ea_block_cache, oe);
1910+
goto clone_block;
1911+
}
1912+
}
18851913
ea_bdebug(bs->bh, "modifying in-place");
18861914
error = ext4_xattr_set_entry(i, s, handle, inode,
18871915
true /* is_block */);
@@ -1897,6 +1925,7 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
18971925
goto cleanup;
18981926
goto inserted;
18991927
}
1928+
clone_block:
19001929
unlock_buffer(bs->bh);
19011930
ea_bdebug(bs->bh, "cloning");
19021931
s->base = kmemdup(BHDR(bs->bh), bs->bh->b_size, GFP_NOFS);
@@ -2002,18 +2031,13 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
20022031
lock_buffer(new_bh);
20032032
/*
20042033
* We have to be careful about races with
2005-
* freeing, rehashing or adding references to
2006-
* xattr block. Once we hold buffer lock xattr
2007-
* block's state is stable so we can check
2008-
* whether the block got freed / rehashed or
2009-
* not. Since we unhash mbcache entry under
2010-
* buffer lock when freeing / rehashing xattr
2011-
* block, checking whether entry is still
2012-
* hashed is reliable. Same rules hold for
2013-
* e_reusable handling.
2034+
* adding references to xattr block. Once we
2035+
* hold buffer lock xattr block's state is
2036+
* stable so we can check the additional
2037+
* reference fits.
20142038
*/
2015-
if (hlist_bl_unhashed(&ce->e_hash_list) ||
2016-
!ce->e_reusable) {
2039+
ref = le32_to_cpu(BHDR(new_bh)->h_refcount) + 1;
2040+
if (ref > EXT4_XATTR_REFCOUNT_MAX) {
20172041
/*
20182042
* Undo everything and check mbcache
20192043
* again.
@@ -2028,9 +2052,8 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
20282052
new_bh = NULL;
20292053
goto inserted;
20302054
}
2031-
ref = le32_to_cpu(BHDR(new_bh)->h_refcount) + 1;
20322055
BHDR(new_bh)->h_refcount = cpu_to_le32(ref);
2033-
if (ref >= EXT4_XATTR_REFCOUNT_MAX)
2056+
if (ref == EXT4_XATTR_REFCOUNT_MAX)
20342057
ce->e_reusable = 0;
20352058
ea_bdebug(new_bh, "reusing; refcount now=%d",
20362059
ref);

0 commit comments

Comments
 (0)