[B][PATCH 6/7] btrfs: extent-tree: kill the BUG_ON() in insert_inline_extent_backref()

Mauricio Faria de Oliveira mfo at canonical.com
Fri Oct 30 15:28:00 UTC 2020

From: Qu Wenruo <wqu at suse.com>

BugLink: https://bugs.launchpad.net/bugs/1902254

With a crafted image, btrfs can panic at insert_inline_extent_backref():

  kernel BUG at fs/btrfs/extent-tree.c:1857!
  invalid opcode: 0000 [#1] SMP PTI
  CPU: 0 PID: 1117 Comm: btrfs-transacti Not tainted 5.0.0-rc8+ #9
  RIP: 0010:insert_inline_extent_backref+0xcc/0xe0
  RSP: 0018:ffffac4dc1287be8 EFLAGS: 00010293
  RAX: 0000000000000000 RBX: 0000000000000007 RCX: 0000000000000001
  RDX: 0000000000001000 RSI: 0000000000000000 RDI: 0000000000000000
  RBP: ffffac4dc1287c28 R08: ffffac4dc1287ab8 R09: ffffac4dc1287ac0
  R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000000
  R13: ffff8febef88a540 R14: ffff8febeaa7bc30 R15: 0000000000000000
  FS: 0000000000000000(0000) GS:ffff8febf7a00000(0000) knlGS:0000000000000000
  CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
  CR2: 00007f663ace94c0 CR3: 0000000235698006 CR4: 00000000000206f0
  Call Trace:
  ? _cond_resched+0x1a/0x50
  ? btrfs_merge_delayed_refs+0xa5/0x330
  ? start_transaction+0x94/0x450
  ? btrfs_cleanup_transaction+0x560/0x560
  ? kthread_destroy_worker+0x50/0x50
  Modules linked in:
  ---[ end trace 2ad8b3de903cf825 ]---

Due to extent tree corruption (still valid by itself, but bad cross
ref), we can allocate an extent which is still in extent tree.  The
offending tree block of that case is from csum tree.  The newly
allocated tree block is also for csum tree.

Then we will try to insert a tree block ref for the existing tree block

For a tree extent item, tree block can never be shared directly by the
same tree twice.  We have such BUG_ON() to prevent such problem, but
this is not a proper error handling.

Replace that BUG_ON() with proper error message and leaf dump for debug

Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=202829
Reviewed-by: Nikolay Borisov <nborisov at suse.com>
Reviewed-by: Josef Bacik <josef at toxicpanda.com>
Signed-off-by: Qu Wenruo <wqu at suse.com>
Signed-off-by: David Sterba <dsterba at suse.com>
(backported from commit 07cce5cf3b489419aa8e87f48a55f4e190a30876)
[mfo: backport:
 - hunk 1: refresh context lines w/ function signatures per lack of:
   - commit 867cc1fbebea ("btrfs: Remove fs_info from lookup_inline_extent_backref")
   - commit 61a18f1c6663 ("btrfs: Remove fs_info argument from update_inline_extent_backref")
 - hunk 2: remove hunk that updates doc introduced by non-existing
   - commit bd3c685ed9fd ("btrfs: Document __btrfs_inc_extent_ref")]
Signed-off-by: Mauricio Faria de Oliveira <mfo at canonical.com>
 fs/btrfs/extent-tree.c | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 54e0fac6226c..2378489a76a7 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -1994,7 +1994,22 @@ int insert_inline_extent_backref(struct btrfs_trans_handle *trans,
 					   bytenr, num_bytes, parent,
 					   root_objectid, owner, offset, 1);
 	if (ret == 0) {
+		/*
+		 * We're adding refs to a tree block we already own, this
+		 * should not happen at all.
+		 */
+			btrfs_crit(trans->fs_info,
+"adding refs to an existing tree ref, bytenr %llu num_bytes %llu root_objectid %llu",
+				   bytenr, num_bytes, root_objectid);
+				WARN_ON(1);
+				btrfs_crit(trans->fs_info,
+			"path->slots[0]=%d path->nodes[0]:", path->slots[0]);
+				btrfs_print_leaf(path->nodes[0]);
+			}
+			return -EUCLEAN;
+		}
 		update_inline_extent_backref(fs_info, path, iref,
 					     refs_to_add, extent_op, NULL);
 	} else if (ret == -ENOENT) {

More information about the kernel-team mailing list