Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

btrfs: do not BUG_ON() when freeing tree block after error

When freeing a tree block, at btrfs_free_tree_block(), if we fail to
create a delayed reference we don't deal with the error and just do a
BUG_ON(). The error most likely to happen is -ENOMEM, and we have a
comment mentioning that only -ENOMEM can happen, but that is not true,
because in case qgroups are enabled any error returned from
btrfs_qgroup_trace_extent_post() (can be -EUCLEAN or anything returned
from btrfs_search_slot() for example) can be propagated back to
btrfs_free_tree_block().

So stop doing a BUG_ON() and return the error to the callers and make
them abort the transaction to prevent leaking space. Syzbot was
triggering this, likely due to memory allocation failure injection.

Reported-by: syzbot+a306f914b4d01b3958fe@syzkaller.appspotmail.com
Link: https://lore.kernel.org/linux-btrfs/000000000000fcba1e05e998263c@google.com/
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>

authored by

Filipe Manana and committed by
David Sterba
bb386803 b7519157

+76 -31
+42 -11
fs/btrfs/ctree.c
··· 620 620 atomic_inc(&cow->refs); 621 621 rcu_assign_pointer(root->node, cow); 622 622 623 - btrfs_free_tree_block(trans, btrfs_root_id(root), buf, 624 - parent_start, last_ref); 623 + ret = btrfs_free_tree_block(trans, btrfs_root_id(root), buf, 624 + parent_start, last_ref); 625 625 free_extent_buffer(buf); 626 626 add_root_to_dirty_list(root); 627 + if (ret < 0) { 628 + btrfs_tree_unlock(cow); 629 + free_extent_buffer(cow); 630 + btrfs_abort_transaction(trans, ret); 631 + return ret; 632 + } 627 633 } else { 628 634 WARN_ON(trans->transid != btrfs_header_generation(parent)); 629 635 ret = btrfs_tree_mod_log_insert_key(parent, parent_slot, ··· 654 648 return ret; 655 649 } 656 650 } 657 - btrfs_free_tree_block(trans, btrfs_root_id(root), buf, 658 - parent_start, last_ref); 651 + ret = btrfs_free_tree_block(trans, btrfs_root_id(root), buf, 652 + parent_start, last_ref); 653 + if (ret < 0) { 654 + btrfs_tree_unlock(cow); 655 + free_extent_buffer(cow); 656 + btrfs_abort_transaction(trans, ret); 657 + return ret; 658 + } 659 659 } 660 660 if (unlock_orig) 661 661 btrfs_tree_unlock(buf); ··· 995 983 free_extent_buffer(mid); 996 984 997 985 root_sub_used_bytes(root); 998 - btrfs_free_tree_block(trans, btrfs_root_id(root), mid, 0, 1); 986 + ret = btrfs_free_tree_block(trans, btrfs_root_id(root), mid, 0, 1); 999 987 /* once for the root ptr */ 1000 988 free_extent_buffer_stale(mid); 989 + if (ret < 0) { 990 + btrfs_abort_transaction(trans, ret); 991 + goto out; 992 + } 1001 993 return 0; 1002 994 } 1003 995 if (btrfs_header_nritems(mid) > ··· 1069 1053 goto out; 1070 1054 } 1071 1055 root_sub_used_bytes(root); 1072 - btrfs_free_tree_block(trans, btrfs_root_id(root), right, 1073 - 0, 1); 1056 + ret = btrfs_free_tree_block(trans, btrfs_root_id(root), 1057 + right, 0, 1); 1074 1058 free_extent_buffer_stale(right); 1075 1059 right = NULL; 1060 + if (ret < 0) { 1061 + btrfs_abort_transaction(trans, ret); 1062 + goto out; 1063 + } 1076 1064 } else { 1077 1065 struct btrfs_disk_key right_key; 1078 1066 btrfs_node_key(right, &right_key, 0); ··· 1131 1111 goto out; 1132 1112 } 1133 1113 root_sub_used_bytes(root); 1134 - btrfs_free_tree_block(trans, btrfs_root_id(root), mid, 0, 1); 1114 + ret = btrfs_free_tree_block(trans, btrfs_root_id(root), mid, 0, 1); 1135 1115 free_extent_buffer_stale(mid); 1136 1116 mid = NULL; 1117 + if (ret < 0) { 1118 + btrfs_abort_transaction(trans, ret); 1119 + goto out; 1120 + } 1137 1121 } else { 1138 1122 /* update the parent key to reflect our changes */ 1139 1123 struct btrfs_disk_key mid_key; ··· 2902 2878 old = root->node; 2903 2879 ret = btrfs_tree_mod_log_insert_root(root->node, c, false); 2904 2880 if (ret < 0) { 2905 - btrfs_free_tree_block(trans, btrfs_root_id(root), c, 0, 1); 2881 + int ret2; 2882 + 2883 + ret2 = btrfs_free_tree_block(trans, btrfs_root_id(root), c, 0, 1); 2884 + if (ret2 < 0) 2885 + btrfs_abort_transaction(trans, ret2); 2906 2886 btrfs_tree_unlock(c); 2907 2887 free_extent_buffer(c); 2908 2888 return ret; ··· 4475 4447 root_sub_used_bytes(root); 4476 4448 4477 4449 atomic_inc(&leaf->refs); 4478 - btrfs_free_tree_block(trans, btrfs_root_id(root), leaf, 0, 1); 4450 + ret = btrfs_free_tree_block(trans, btrfs_root_id(root), leaf, 0, 1); 4479 4451 free_extent_buffer_stale(leaf); 4480 - return 0; 4452 + if (ret < 0) 4453 + btrfs_abort_transaction(trans, ret); 4454 + 4455 + return ret; 4481 4456 } 4482 4457 /* 4483 4458 * delete the item at the leaf level in path. If that empties
+14 -10
fs/btrfs/extent-tree.c
··· 3419 3419 return 0; 3420 3420 } 3421 3421 3422 - void btrfs_free_tree_block(struct btrfs_trans_handle *trans, 3423 - u64 root_id, 3424 - struct extent_buffer *buf, 3425 - u64 parent, int last_ref) 3422 + int btrfs_free_tree_block(struct btrfs_trans_handle *trans, 3423 + u64 root_id, 3424 + struct extent_buffer *buf, 3425 + u64 parent, int last_ref) 3426 3426 { 3427 3427 struct btrfs_fs_info *fs_info = trans->fs_info; 3428 3428 struct btrfs_block_group *bg; ··· 3449 3449 btrfs_init_tree_ref(&generic_ref, btrfs_header_level(buf), 0, false); 3450 3450 btrfs_ref_tree_mod(fs_info, &generic_ref); 3451 3451 ret = btrfs_add_delayed_tree_ref(trans, &generic_ref, NULL); 3452 - BUG_ON(ret); /* -ENOMEM */ 3452 + if (ret < 0) 3453 + return ret; 3453 3454 } 3454 3455 3455 3456 if (!last_ref) 3456 - return; 3457 + return 0; 3457 3458 3458 3459 if (btrfs_header_generation(buf) != trans->transid) 3459 3460 goto out; ··· 3511 3510 * matter anymore. 3512 3511 */ 3513 3512 clear_bit(EXTENT_BUFFER_CORRUPT, &buf->bflags); 3513 + return 0; 3514 3514 } 3515 3515 3516 3516 /* Can return -ENOMEM */ ··· 5756 5754 struct walk_control *wc) 5757 5755 { 5758 5756 struct btrfs_fs_info *fs_info = root->fs_info; 5759 - int ret; 5757 + int ret = 0; 5760 5758 int level = wc->level; 5761 5759 struct extent_buffer *eb = path->nodes[level]; 5762 5760 u64 parent = 0; ··· 5851 5849 goto owner_mismatch; 5852 5850 } 5853 5851 5854 - btrfs_free_tree_block(trans, btrfs_root_id(root), eb, parent, 5855 - wc->refs[level] == 1); 5852 + ret = btrfs_free_tree_block(trans, btrfs_root_id(root), eb, parent, 5853 + wc->refs[level] == 1); 5854 + if (ret < 0) 5855 + btrfs_abort_transaction(trans, ret); 5856 5856 out: 5857 5857 wc->refs[level] = 0; 5858 5858 wc->flags[level] = 0; 5859 - return 0; 5859 + return ret; 5860 5860 5861 5861 owner_mismatch: 5862 5862 btrfs_err_rl(fs_info, "unexpected tree owner, have %llu expect %llu",
+4 -4
fs/btrfs/extent-tree.h
··· 127 127 u64 empty_size, 128 128 u64 reloc_src_root, 129 129 enum btrfs_lock_nesting nest); 130 - void btrfs_free_tree_block(struct btrfs_trans_handle *trans, 131 - u64 root_id, 132 - struct extent_buffer *buf, 133 - u64 parent, int last_ref); 130 + int btrfs_free_tree_block(struct btrfs_trans_handle *trans, 131 + u64 root_id, 132 + struct extent_buffer *buf, 133 + u64 parent, int last_ref); 134 134 int btrfs_alloc_reserved_file_extent(struct btrfs_trans_handle *trans, 135 135 struct btrfs_root *root, u64 owner, 136 136 u64 offset, u64 ram_bytes,
+7 -3
fs/btrfs/free-space-tree.c
··· 1300 1300 btrfs_tree_lock(free_space_root->node); 1301 1301 btrfs_clear_buffer_dirty(trans, free_space_root->node); 1302 1302 btrfs_tree_unlock(free_space_root->node); 1303 - btrfs_free_tree_block(trans, btrfs_root_id(free_space_root), 1304 - free_space_root->node, 0, 1); 1305 - 1303 + ret = btrfs_free_tree_block(trans, btrfs_root_id(free_space_root), 1304 + free_space_root->node, 0, 1); 1306 1305 btrfs_put_root(free_space_root); 1306 + if (ret < 0) { 1307 + btrfs_abort_transaction(trans, ret); 1308 + btrfs_end_transaction(trans); 1309 + return ret; 1310 + } 1307 1311 1308 1312 return btrfs_commit_transaction(trans); 1309 1313 }
+5 -1
fs/btrfs/ioctl.c
··· 714 714 ret = btrfs_insert_root(trans, fs_info->tree_root, &key, 715 715 root_item); 716 716 if (ret) { 717 + int ret2; 718 + 717 719 /* 718 720 * Since we don't abort the transaction in this case, free the 719 721 * tree block so that we don't leak space and leave the ··· 726 724 btrfs_tree_lock(leaf); 727 725 btrfs_clear_buffer_dirty(trans, leaf); 728 726 btrfs_tree_unlock(leaf); 729 - btrfs_free_tree_block(trans, objectid, leaf, 0, 1); 727 + ret2 = btrfs_free_tree_block(trans, objectid, leaf, 0, 1); 728 + if (ret2 < 0) 729 + btrfs_abort_transaction(trans, ret2); 730 730 free_extent_buffer(leaf); 731 731 goto out; 732 732 }
+4 -2
fs/btrfs/qgroup.c
··· 1441 1441 btrfs_tree_lock(quota_root->node); 1442 1442 btrfs_clear_buffer_dirty(trans, quota_root->node); 1443 1443 btrfs_tree_unlock(quota_root->node); 1444 - btrfs_free_tree_block(trans, btrfs_root_id(quota_root), 1445 - quota_root->node, 0, 1); 1444 + ret = btrfs_free_tree_block(trans, btrfs_root_id(quota_root), 1445 + quota_root->node, 0, 1); 1446 1446 1447 + if (ret < 0) 1448 + btrfs_abort_transaction(trans, ret); 1447 1449 1448 1450 out: 1449 1451 btrfs_put_root(quota_root);