Skip to content

Commit 65c6e82

Browse files
adam900710kdave
authored andcommitted
btrfs: Handle owner mismatch gracefully when walking up tree
[BUG] When mounting certain crafted image, btrfs will trigger kernel BUG_ON() when trying to recover balance: kernel BUG at fs/btrfs/extent-tree.c:8956! invalid opcode: 0000 [#1] PREEMPT SMP NOPTI CPU: 1 PID: 662 Comm: mount Not tainted 4.18.0-rc1-custom+ thesofproject#10 RIP: 0010:walk_up_proc+0x336/0x480 [btrfs] RSP: 0018:ffffb53540c9b890 EFLAGS: 00010202 Call Trace: walk_up_tree+0x172/0x1f0 [btrfs] btrfs_drop_snapshot+0x3a4/0x830 [btrfs] merge_reloc_roots+0xe1/0x1d0 [btrfs] btrfs_recover_relocation+0x3ea/0x420 [btrfs] open_ctree+0x1af3/0x1dd0 [btrfs] btrfs_mount_root+0x66b/0x740 [btrfs] mount_fs+0x3b/0x16a vfs_kern_mount.part.9+0x54/0x140 btrfs_mount+0x16d/0x890 [btrfs] mount_fs+0x3b/0x16a vfs_kern_mount.part.9+0x54/0x140 do_mount+0x1fd/0xda0 ksys_mount+0xba/0xd0 __x64_sys_mount+0x21/0x30 do_syscall_64+0x60/0x210 entry_SYSCALL_64_after_hwframe+0x49/0xbe [CAUSE] Extent tree corruption. In this particular case, reloc tree root's owner is DATA_RELOC_TREE (should be TREE_RELOC), thus its backref is corrupted and we failed the owner check in walk_up_tree(). [FIX] It's pretty hard to take care of every extent tree corruption, but at least we can remove such BUG_ON() and exit more gracefully. And since in this particular image, DATA_RELOC_TREE and TREE_RELOC share the same root (which is obviously invalid), we needs to make __del_reloc_root() more robust to detect such invalid sharing to avoid possible NULL dereference as root->node can be NULL in this case. Link: https://bugzilla.kernel.org/show_bug.cgi?id=200411 Reported-by: Xu Wen <wen.xu@gatech.edu> CC: stable@vger.kernel.org # 4.4+ Signed-off-by: Qu Wenruo <wqu@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 45128b0 commit 65c6e82

File tree

2 files changed

+13
-7
lines changed

2 files changed

+13
-7
lines changed

fs/btrfs/extent-tree.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8759,22 +8759,26 @@ static noinline int walk_up_proc(struct btrfs_trans_handle *trans,
87598759
if (eb == root->node) {
87608760
if (wc->flags[level] & BTRFS_BLOCK_FLAG_FULL_BACKREF)
87618761
parent = eb->start;
8762-
else
8763-
BUG_ON(root->root_key.objectid !=
8764-
btrfs_header_owner(eb));
8762+
else if (root->root_key.objectid != btrfs_header_owner(eb))
8763+
goto owner_mismatch;
87658764
} else {
87668765
if (wc->flags[level + 1] & BTRFS_BLOCK_FLAG_FULL_BACKREF)
87678766
parent = path->nodes[level + 1]->start;
8768-
else
8769-
BUG_ON(root->root_key.objectid !=
8770-
btrfs_header_owner(path->nodes[level + 1]));
8767+
else if (root->root_key.objectid !=
8768+
btrfs_header_owner(path->nodes[level + 1]))
8769+
goto owner_mismatch;
87718770
}
87728771

87738772
btrfs_free_tree_block(trans, root, eb, parent, wc->refs[level] == 1);
87748773
out:
87758774
wc->refs[level] = 0;
87768775
wc->flags[level] = 0;
87778776
return 0;
8777+
8778+
owner_mismatch:
8779+
btrfs_err_rl(fs_info, "unexpected tree owner, have %llu expect %llu",
8780+
btrfs_header_owner(eb), root->root_key.objectid);
8781+
return -EUCLEAN;
87788782
}
87798783

87808784
static noinline int walk_down_tree(struct btrfs_trans_handle *trans,
@@ -8828,6 +8832,8 @@ static noinline int walk_up_tree(struct btrfs_trans_handle *trans,
88288832
ret = walk_up_proc(trans, root, path, wc);
88298833
if (ret > 0)
88308834
return 0;
8835+
if (ret < 0)
8836+
return ret;
88318837

88328838
if (path->locks[level]) {
88338839
btrfs_tree_unlock_rw(path->nodes[level],

fs/btrfs/relocation.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1282,7 +1282,7 @@ static void __del_reloc_root(struct btrfs_root *root)
12821282
struct mapping_node *node = NULL;
12831283
struct reloc_control *rc = fs_info->reloc_ctl;
12841284

1285-
if (rc) {
1285+
if (rc && root->node) {
12861286
spin_lock(&rc->reloc_root_tree.lock);
12871287
rb_node = tree_search(&rc->reloc_root_tree.rb_root,
12881288
root->node->start);

0 commit comments

Comments
 (0)