Skip to content

Commit 05d7ce5

Browse files
adam900710kdave
authored andcommitted
btrfs: exit gracefully if reloc roots don't match
[BUG] Syzbot reported a crash that an ASSERT() got triggered inside prepare_to_merge(). [CAUSE] The root cause of the triggered ASSERT() is we can have a race between quota tree creation and relocation. This leads us to create a duplicated quota tree in the btrfs_read_fs_root() path, and since it's treated as fs tree, it would have ROOT_SHAREABLE flag, causing us to create a reloc tree for it. The bug itself is fixed by a dedicated patch for it, but this already taught us the ASSERT() is not something straightforward for developers. [ENHANCEMENT] Instead of using an ASSERT(), let's handle it gracefully and output extra info about the mismatch reloc roots to help debug. Also with the above ASSERT() removed, we can trigger ASSERT(0)s inside merge_reloc_roots() later. Also replace those ASSERT(0)s with WARN_ON()s. CC: stable@vger.kernel.org # 5.15+ Reported-by: syzbot+ae97a827ae1c3336bbb4@syzkaller.appspotmail.com Reviewed-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 773e722 commit 05d7ce5

File tree

1 file changed

+37
-8
lines changed

1 file changed

+37
-8
lines changed

fs/btrfs/relocation.c

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1916,7 +1916,39 @@ int prepare_to_merge(struct reloc_control *rc, int err)
19161916
err = PTR_ERR(root);
19171917
break;
19181918
}
1919-
ASSERT(root->reloc_root == reloc_root);
1919+
1920+
if (unlikely(root->reloc_root != reloc_root)) {
1921+
if (root->reloc_root) {
1922+
btrfs_err(fs_info,
1923+
"reloc tree mismatch, root %lld has reloc root key (%lld %u %llu) gen %llu, expect reloc root key (%lld %u %llu) gen %llu",
1924+
root->root_key.objectid,
1925+
root->reloc_root->root_key.objectid,
1926+
root->reloc_root->root_key.type,
1927+
root->reloc_root->root_key.offset,
1928+
btrfs_root_generation(
1929+
&root->reloc_root->root_item),
1930+
reloc_root->root_key.objectid,
1931+
reloc_root->root_key.type,
1932+
reloc_root->root_key.offset,
1933+
btrfs_root_generation(
1934+
&reloc_root->root_item));
1935+
} else {
1936+
btrfs_err(fs_info,
1937+
"reloc tree mismatch, root %lld has no reloc root, expect reloc root key (%lld %u %llu) gen %llu",
1938+
root->root_key.objectid,
1939+
reloc_root->root_key.objectid,
1940+
reloc_root->root_key.type,
1941+
reloc_root->root_key.offset,
1942+
btrfs_root_generation(
1943+
&reloc_root->root_item));
1944+
}
1945+
list_add(&reloc_root->root_list, &reloc_roots);
1946+
btrfs_put_root(root);
1947+
btrfs_abort_transaction(trans, -EUCLEAN);
1948+
if (!err)
1949+
err = -EUCLEAN;
1950+
break;
1951+
}
19201952

19211953
/*
19221954
* set reference count to 1, so btrfs_recover_relocation
@@ -1989,7 +2021,7 @@ void merge_reloc_roots(struct reloc_control *rc)
19892021
root = btrfs_get_fs_root(fs_info, reloc_root->root_key.offset,
19902022
false);
19912023
if (btrfs_root_refs(&reloc_root->root_item) > 0) {
1992-
if (IS_ERR(root)) {
2024+
if (WARN_ON(IS_ERR(root))) {
19932025
/*
19942026
* For recovery we read the fs roots on mount,
19952027
* and if we didn't find the root then we marked
@@ -1998,17 +2030,14 @@ void merge_reloc_roots(struct reloc_control *rc)
19982030
* memory. However there's no reason we can't
19992031
* handle the error properly here just in case.
20002032
*/
2001-
ASSERT(0);
20022033
ret = PTR_ERR(root);
20032034
goto out;
20042035
}
2005-
if (root->reloc_root != reloc_root) {
2036+
if (WARN_ON(root->reloc_root != reloc_root)) {
20062037
/*
2007-
* This is actually impossible without something
2008-
* going really wrong (like weird race condition
2009-
* or cosmic rays).
2038+
* This can happen if on-disk metadata has some
2039+
* corruption, e.g. bad reloc tree key offset.
20102040
*/
2011-
ASSERT(0);
20122041
ret = -EINVAL;
20132042
goto out;
20142043
}

0 commit comments

Comments
 (0)