All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/7] add sanity check for extent inline ref type
@ 2017-08-18 21:15 Liu Bo
  2017-08-18 21:15 ` [PATCH v3 1/7] Btrfs: add a helper to retrive " Liu Bo
                   ` (7 more replies)
  0 siblings, 8 replies; 16+ messages in thread
From: Liu Bo @ 2017-08-18 21:15 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

An invalid extent inline ref type could be read from a btrfs image and
it ends up with a panic[1], this set is to deal with the insane value
gracefully in patch 1-2 and clean up BUG() in the code in patch 3-6.

Patch 7 adds one more check to see if the ref is a valid shared one.

I'm not sure in the real world what may result in this corruption, but
I've seen several reports on the ML about __btrfs_free_extent saying
something was missing (or simply wrong), while testing this set with
btrfs-corrupt-block, I found that switching ref type could end up that
situation as well, eg. a data extent's ref type
(BTRFS_EXTENT_DATA_REF_KEY) is switched to (BTRFS_TREE_BLOCK_REF_KEY).
Hopefully this can give people more sights next time when that
happens.

[1]:https://www.spinics.net/lists/linux-btrfs/msg65646.html

v3:
- btrfs_inline_ref_types -> btrfs_inline_ref_type
- convert WARN(1) to btrfs_err so that we know which btrfs has that error.

v2:
- add enum type and return BTRFS_REF_TYPE_INVALID instead of -EINVAL.
- remove one more BUG_ON which is in __add_tree_block.
- add validation check for shared refs.
- improve btrfs_print_leaf to show which refs has something wrong.

Liu Bo (7):
  Btrfs: add a helper to retrive extent inline ref type
  Btrfs: convert to use btrfs_get_extent_inline_ref_type
  Btrfs: remove BUG() in btrfs_extent_inline_ref_size
  Btrfs: remove BUG() in print_extent_item
  Btrfs: remove BUG() in add_data_reference
  Btrfs: remove BUG_ON in __add_tree_block
  Btrfs: add one more sanity check for shared ref type

 fs/btrfs/backref.c     | 11 ++++--
 fs/btrfs/ctree.h       | 12 ++++++-
 fs/btrfs/extent-tree.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++----
 fs/btrfs/print-tree.c  | 28 ++++++++++++---
 fs/btrfs/relocation.c  | 30 +++++++++++++---
 5 files changed, 157 insertions(+), 18 deletions(-)

-- 
2.9.4


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v3 1/7] Btrfs: add a helper to retrive extent inline ref type
  2017-08-18 21:15 [PATCH v3 0/7] add sanity check for extent inline ref type Liu Bo
@ 2017-08-18 21:15 ` Liu Bo
  2017-08-21 14:38   ` David Sterba
  2017-08-18 21:15 ` [PATCH v3 2/7] Btrfs: convert to use btrfs_get_extent_inline_ref_type Liu Bo
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 16+ messages in thread
From: Liu Bo @ 2017-08-18 21:15 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

An invalid value of extent inline ref type may be read from a
malicious image which may force btrfs to crash.

This adds a helper which does sanity check for the ref type, so we can
know if it's sane, return type if so, otherwise return an error.

Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
---
 fs/btrfs/ctree.h       | 11 +++++++++++
 fs/btrfs/extent-tree.c | 37 +++++++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+)

diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 3f3eb7b..714e779 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -2561,6 +2561,17 @@ static inline gfp_t btrfs_alloc_write_mask(struct address_space *mapping)
 
 /* extent-tree.c */
 
+enum btrfs_inline_ref_type {
+	BTRFS_REF_TYPE_INVALID =	 0,
+	BTRFS_REF_TYPE_BLOCK =		 1,
+	BTRFS_REF_TYPE_DATA =		 2,
+	BTRFS_REF_TYPE_ANY =		 3,
+};
+
+int btrfs_get_extent_inline_ref_type(struct extent_buffer *eb,
+				     struct btrfs_extent_inline_ref *iref,
+				     enum btrfs_inline_ref_type is_data);
+
 u64 btrfs_csum_bytes_to_leaves(struct btrfs_fs_info *fs_info, u64 csum_bytes);
 
 static inline u64 btrfs_calc_trans_metadata_size(struct btrfs_fs_info *fs_info,
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index e3b0b41..c540578 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -1148,6 +1148,43 @@ static int convert_extent_item_v0(struct btrfs_trans_handle *trans,
 }
 #endif
 
+/*
+ * is_data == BTRFS_REF_TYPE_BLOCK, tree block type is required,
+ * is_data == BTRFS_REF_TYPE_DATA, data type is requried,
+ * is_data == BTRFS_REF_TYPE_ANY, either type is OK.
+ */
+int btrfs_get_extent_inline_ref_type(struct extent_buffer *eb,
+				     struct btrfs_extent_inline_ref *iref,
+				     enum btrfs_inline_ref_type is_data)
+{
+	int type = btrfs_extent_inline_ref_type(eb, iref);
+
+	if (type == BTRFS_TREE_BLOCK_REF_KEY ||
+	    type == BTRFS_SHARED_BLOCK_REF_KEY ||
+	    type == BTRFS_SHARED_DATA_REF_KEY ||
+	    type == BTRFS_EXTENT_DATA_REF_KEY) {
+		if (is_data == BTRFS_REF_TYPE_BLOCK) {
+			if (type == BTRFS_TREE_BLOCK_REF_KEY ||
+			    type == BTRFS_SHARED_BLOCK_REF_KEY)
+				return type;
+		} else if (is_data == BTRFS_REF_TYPE_DATA) {
+			if (type == BTRFS_EXTENT_DATA_REF_KEY ||
+			    type == BTRFS_SHARED_DATA_REF_KEY)
+				return type;
+		} else {
+			ASSERT(is_data == BTRFS_REF_TYPE_ANY);
+			return type;
+		}
+	}
+
+	btrfs_print_leaf(eb->fs_info, eb);
+	btrfs_err(eb->fs_info, "eb %llu invalid extent inline ref type %d",
+		  eb->start, type);
+	WARN_ON(1);
+
+	return BTRFS_REF_TYPE_INVALID;
+}
+
 static u64 hash_extent_data_ref(u64 root_objectid, u64 owner, u64 offset)
 {
 	u32 high_crc = ~(u32)0;
-- 
2.9.4


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 2/7] Btrfs: convert to use btrfs_get_extent_inline_ref_type
  2017-08-18 21:15 [PATCH v3 0/7] add sanity check for extent inline ref type Liu Bo
  2017-08-18 21:15 ` [PATCH v3 1/7] Btrfs: add a helper to retrive " Liu Bo
@ 2017-08-18 21:15 ` Liu Bo
  2017-08-21 14:52   ` David Sterba
  2017-08-18 21:15 ` [PATCH v3 3/7] Btrfs: remove BUG() in btrfs_extent_inline_ref_size Liu Bo
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 16+ messages in thread
From: Liu Bo @ 2017-08-18 21:15 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

Since we have a helper which can do sanity check, this converts all
btrfs_extent_inline_ref_type to it.

Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
---
 fs/btrfs/backref.c     | 11 +++++++++--
 fs/btrfs/extent-tree.c | 36 ++++++++++++++++++++++++++++++------
 fs/btrfs/relocation.c  | 13 +++++++++++--
 3 files changed, 50 insertions(+), 10 deletions(-)

diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
index f723c11..1b3d9df 100644
--- a/fs/btrfs/backref.c
+++ b/fs/btrfs/backref.c
@@ -1012,7 +1012,11 @@ static int __add_inline_refs(struct btrfs_path *path, u64 bytenr,
 		int type;
 
 		iref = (struct btrfs_extent_inline_ref *)ptr;
-		type = btrfs_extent_inline_ref_type(leaf, iref);
+		type = btrfs_get_extent_inline_ref_type(leaf, iref,
+							BTRFS_REF_TYPE_ANY);
+		if (type == BTRFS_REF_TYPE_INVALID)
+			return -EINVAL;
+
 		offset = btrfs_extent_inline_ref_offset(leaf, iref);
 
 		switch (type) {
@@ -1908,7 +1912,10 @@ static int __get_extent_inline_ref(unsigned long *ptr, struct extent_buffer *eb,
 
 	end = (unsigned long)ei + item_size;
 	*out_eiref = (struct btrfs_extent_inline_ref *)(*ptr);
-	*out_type = btrfs_extent_inline_ref_type(eb, *out_eiref);
+	*out_type = btrfs_get_extent_inline_ref_type(eb, *out_eiref,
+						     BTRFS_REF_TYPE_ANY);
+	if (*out_type == BTRFS_REF_TYPE_INVALID)
+		return -EINVAL;
 
 	*ptr += btrfs_extent_inline_ref_size(*out_type);
 	WARN_ON(*ptr > end);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index c540578..d75129a 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -1454,12 +1454,18 @@ static noinline u32 extent_data_ref_count(struct btrfs_path *path,
 	struct btrfs_extent_data_ref *ref1;
 	struct btrfs_shared_data_ref *ref2;
 	u32 num_refs = 0;
+	int type;
 
 	leaf = path->nodes[0];
 	btrfs_item_key_to_cpu(leaf, &key, path->slots[0]);
 	if (iref) {
-		if (btrfs_extent_inline_ref_type(leaf, iref) ==
-		    BTRFS_EXTENT_DATA_REF_KEY) {
+		/*
+		 * If type is invalid, we should have bailed out earlier than
+		 * this call.
+		 */
+		type = btrfs_get_extent_inline_ref_type(leaf, iref, BTRFS_REF_TYPE_DATA);
+		ASSERT(type != BTRFS_REF_TYPE_INVALID);
+		if (type == BTRFS_EXTENT_DATA_REF_KEY) {
 			ref1 = (struct btrfs_extent_data_ref *)(&iref->offset);
 			num_refs = btrfs_extent_data_ref_count(leaf, ref1);
 		} else {
@@ -1620,6 +1626,7 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
 	int ret;
 	int err = 0;
 	bool skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA);
+	int needed;
 
 	key.objectid = bytenr;
 	key.type = BTRFS_EXTENT_ITEM_KEY;
@@ -1711,6 +1718,11 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
 		BUG_ON(ptr > end);
 	}
 
+	if (owner >= BTRFS_FIRST_FREE_OBJECTID)
+		needed = BTRFS_REF_TYPE_DATA;
+	else
+		needed = BTRFS_REF_TYPE_BLOCK;
+
 	err = -ENOENT;
 	while (1) {
 		if (ptr >= end) {
@@ -1718,7 +1730,12 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
 			break;
 		}
 		iref = (struct btrfs_extent_inline_ref *)ptr;
-		type = btrfs_extent_inline_ref_type(leaf, iref);
+		type = btrfs_get_extent_inline_ref_type(leaf, iref, needed);
+		if (type == BTRFS_REF_TYPE_INVALID) {
+			err = -EINVAL;
+			goto out;
+		}
+
 		if (want < type)
 			break;
 		if (want > type) {
@@ -1910,7 +1927,12 @@ void update_inline_extent_backref(struct btrfs_fs_info *fs_info,
 	if (extent_op)
 		__run_delayed_extent_op(extent_op, leaf, ei);
 
-	type = btrfs_extent_inline_ref_type(leaf, iref);
+	/*
+	 * If type is invalid, we should have bailed out after
+	 * lookup_inline_extent_backref().
+	 */
+	type = btrfs_get_extent_inline_ref_type(leaf, iref, BTRFS_REF_TYPE_ANY);
+	ASSERT(type != BTRFS_REF_TYPE_INVALID);
 
 	if (type == BTRFS_EXTENT_DATA_REF_KEY) {
 		dref = (struct btrfs_extent_data_ref *)(&iref->offset);
@@ -3195,6 +3217,7 @@ static noinline int check_committed_ref(struct btrfs_root *root,
 	struct btrfs_extent_item *ei;
 	struct btrfs_key key;
 	u32 item_size;
+	int type;
 	int ret;
 
 	key.objectid = bytenr;
@@ -3236,8 +3259,9 @@ static noinline int check_committed_ref(struct btrfs_root *root,
 		goto out;
 
 	iref = (struct btrfs_extent_inline_ref *)(ei + 1);
-	if (btrfs_extent_inline_ref_type(leaf, iref) !=
-	    BTRFS_EXTENT_DATA_REF_KEY)
+
+	type = btrfs_get_extent_inline_ref_type(leaf, iref, BTRFS_REF_TYPE_DATA);
+	if (type != BTRFS_EXTENT_DATA_REF_KEY)
 		goto out;
 
 	ref = (struct btrfs_extent_data_ref *)(&iref->offset);
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index 65661d1..f0bef3c 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -799,9 +799,17 @@ struct backref_node *build_backref_tree(struct reloc_control *rc,
 		if (ptr < end) {
 			/* update key for inline back ref */
 			struct btrfs_extent_inline_ref *iref;
+			int type;
 			iref = (struct btrfs_extent_inline_ref *)ptr;
-			key.type = btrfs_extent_inline_ref_type(eb, iref);
+			type = btrfs_get_extent_inline_ref_type(eb, iref,
+							BTRFS_REF_TYPE_BLOCK);
+			if (type == BTRFS_REF_TYPE_INVALID) {
+				err = -EINVAL;
+				goto out;
+			}
+			key.type = type;
 			key.offset = btrfs_extent_inline_ref_offset(eb, iref);
+
 			WARN_ON(key.type != BTRFS_TREE_BLOCK_REF_KEY &&
 				key.type != BTRFS_SHARED_BLOCK_REF_KEY);
 		}
@@ -3755,7 +3763,8 @@ int add_data_references(struct reloc_control *rc,
 
 	while (ptr < end) {
 		iref = (struct btrfs_extent_inline_ref *)ptr;
-		key.type = btrfs_extent_inline_ref_type(eb, iref);
+		key.type = btrfs_get_extent_inline_ref_type(eb, iref,
+							BTRFS_REF_TYPE_DATA);
 		if (key.type == BTRFS_SHARED_DATA_REF_KEY) {
 			key.offset = btrfs_extent_inline_ref_offset(eb, iref);
 			ret = __add_tree_block(rc, key.offset, blocksize,
-- 
2.9.4


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 3/7] Btrfs: remove BUG() in btrfs_extent_inline_ref_size
  2017-08-18 21:15 [PATCH v3 0/7] add sanity check for extent inline ref type Liu Bo
  2017-08-18 21:15 ` [PATCH v3 1/7] Btrfs: add a helper to retrive " Liu Bo
  2017-08-18 21:15 ` [PATCH v3 2/7] Btrfs: convert to use btrfs_get_extent_inline_ref_type Liu Bo
@ 2017-08-18 21:15 ` Liu Bo
  2017-08-21 14:52   ` David Sterba
  2017-08-18 21:15 ` [PATCH v3 4/7] Btrfs: remove BUG() in print_extent_item Liu Bo
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 16+ messages in thread
From: Liu Bo @ 2017-08-18 21:15 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

Now that btrfs_get_extent_inline_ref_type() can report if type is a
valid one and all callers can gracefully deal with that, we don't need
to crash here.

Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
---
 fs/btrfs/ctree.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 714e779..38b0735 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -1799,7 +1799,6 @@ static inline u32 btrfs_extent_inline_ref_size(int type)
 	if (type == BTRFS_EXTENT_DATA_REF_KEY)
 		return sizeof(struct btrfs_extent_data_ref) +
 		       offsetof(struct btrfs_extent_inline_ref, offset);
-	BUG();
 	return 0;
 }
 
-- 
2.9.4


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 4/7] Btrfs: remove BUG() in print_extent_item
  2017-08-18 21:15 [PATCH v3 0/7] add sanity check for extent inline ref type Liu Bo
                   ` (2 preceding siblings ...)
  2017-08-18 21:15 ` [PATCH v3 3/7] Btrfs: remove BUG() in btrfs_extent_inline_ref_size Liu Bo
@ 2017-08-18 21:15 ` Liu Bo
  2017-08-21 14:53   ` David Sterba
  2017-08-18 21:15 ` [PATCH v3 5/7] Btrfs: remove BUG() in add_data_reference Liu Bo
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 16+ messages in thread
From: Liu Bo @ 2017-08-18 21:15 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

btrfs_print_leaf() is used in btrfs_get_extent_inline_ref_type, so
here we really want to print the invalid value of ref type instead of
causing a kernel panic.

Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
---
 fs/btrfs/print-tree.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/print-tree.c b/fs/btrfs/print-tree.c
index fcae61e..0e52e47 100644
--- a/fs/btrfs/print-tree.c
+++ b/fs/btrfs/print-tree.c
@@ -121,7 +121,10 @@ static void print_extent_item(struct extent_buffer *eb, int slot, int type)
 			       offset, btrfs_shared_data_ref_count(eb, sref));
 			break;
 		default:
-			BUG();
+			btrfs_err(eb->fs_info,
+				  "extent %llu has invalid ref type %d\n",
+				  eb->start, type);
+			return;
 		}
 		ptr += btrfs_extent_inline_ref_size(type);
 	}
-- 
2.9.4


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 5/7] Btrfs: remove BUG() in add_data_reference
  2017-08-18 21:15 [PATCH v3 0/7] add sanity check for extent inline ref type Liu Bo
                   ` (3 preceding siblings ...)
  2017-08-18 21:15 ` [PATCH v3 4/7] Btrfs: remove BUG() in print_extent_item Liu Bo
@ 2017-08-18 21:15 ` Liu Bo
  2017-08-21 14:53   ` David Sterba
  2017-08-18 21:15 ` [PATCH v3 6/7] Btrfs: remove BUG_ON in __add_tree_block Liu Bo
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 16+ messages in thread
From: Liu Bo @ 2017-08-18 21:15 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

Now that we have a helper to report invalid value of extent inline ref
type, we need to quit gracefully instead of throwing out a kernel panic.

Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
---
 fs/btrfs/relocation.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index f0bef3c..53fc798 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -3774,7 +3774,10 @@ int add_data_references(struct reloc_control *rc,
 			ret = find_data_references(rc, extent_key,
 						   eb, dref, blocks);
 		} else {
-			BUG();
+			ret = -EINVAL;
+			btrfs_err(rc->extent_root->fs_info,
+		     "extent %llu slot %d has an invalid inline ref type",
+			     eb->start, path->slots[0]);
 		}
 		if (ret) {
 			err = ret;
-- 
2.9.4


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 6/7] Btrfs: remove BUG_ON in __add_tree_block
  2017-08-18 21:15 [PATCH v3 0/7] add sanity check for extent inline ref type Liu Bo
                   ` (4 preceding siblings ...)
  2017-08-18 21:15 ` [PATCH v3 5/7] Btrfs: remove BUG() in add_data_reference Liu Bo
@ 2017-08-18 21:15 ` Liu Bo
  2017-08-21 14:59   ` David Sterba
  2017-08-18 21:15 ` [PATCH v3 7/7] Btrfs: add one more sanity check for shared ref type Liu Bo
  2017-08-21 15:32 ` [PATCH v3 0/7] add sanity check for extent inline " David Sterba
  7 siblings, 1 reply; 16+ messages in thread
From: Liu Bo @ 2017-08-18 21:15 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

The BUG_ON() can be triggered when the caller is processing an invalid
extent inline ref, e.g.

a shared data ref is offered instead of a extent data ref, such that
it tries to find a non-exist tree block and then btrfs_search_slot
returns 1 for no such item.

This replaces the BUG_ON() with a WARN() followed by calling
btrfs_print_leaf() to show more details about what's going on and
returning -EINVAL to upper callers.

Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
---
 fs/btrfs/relocation.c | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index 53fc798..71d38dd 100644
--- a/fs/btrfs/relocation.c
+++ b/fs/btrfs/relocation.c
@@ -32,6 +32,7 @@
 #include "free-space-cache.h"
 #include "inode-map.h"
 #include "qgroup.h"
+#include "print-tree.h"
 
 /*
  * backref_node, mapping_node and tree_block start with this
@@ -3485,7 +3486,16 @@ static int __add_tree_block(struct reloc_control *rc,
 			goto again;
 		}
 	}
-	BUG_ON(ret);
+	if (ret) {
+		ASSERT(ret == 1);
+		btrfs_print_leaf(rc->extent_root->fs_info, path->nodes[0]);
+		btrfs_err(fs_info,
+	     "tree block extent item (%llu) is not found in extent tree",
+		     bytenr);
+		WARN_ON(1);
+		ret = -EINVAL;
+		goto out;
+	}
 
 	ret = add_tree_block(rc, &key, path, blocks);
 out:
-- 
2.9.4


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 7/7] Btrfs: add one more sanity check for shared ref type
  2017-08-18 21:15 [PATCH v3 0/7] add sanity check for extent inline ref type Liu Bo
                   ` (5 preceding siblings ...)
  2017-08-18 21:15 ` [PATCH v3 6/7] Btrfs: remove BUG_ON in __add_tree_block Liu Bo
@ 2017-08-18 21:15 ` Liu Bo
  2017-08-21 15:00   ` David Sterba
  2017-08-21 15:32 ` [PATCH v3 0/7] add sanity check for extent inline " David Sterba
  7 siblings, 1 reply; 16+ messages in thread
From: Liu Bo @ 2017-08-18 21:15 UTC (permalink / raw)
  To: linux-btrfs; +Cc: David Sterba

Every shared ref has a parent tree block, which can be get from
btrfs_extent_inline_ref_offset().  And the tree block must be aligned
to the nodesize, so we'd know this inline ref is not valid if this
block's bytenr is not aligned to the nodesize, in which case, most
likely the ref type has been misused.

This adds the above mentioned check and also updates
print_extent_item() called by btrfs_print_leaf() to point out the
invalid ref while printing the tree structure.

Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
---
 fs/btrfs/extent-tree.c | 29 +++++++++++++++++++++++++----
 fs/btrfs/print-tree.c  | 27 +++++++++++++++++++++------
 2 files changed, 46 insertions(+), 10 deletions(-)

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index d75129a..13264cd 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -1158,19 +1158,40 @@ int btrfs_get_extent_inline_ref_type(struct extent_buffer *eb,
 				     enum btrfs_inline_ref_type is_data)
 {
 	int type = btrfs_extent_inline_ref_type(eb, iref);
+	u64 offset = btrfs_extent_inline_ref_offset(eb, iref);
 
 	if (type == BTRFS_TREE_BLOCK_REF_KEY ||
 	    type == BTRFS_SHARED_BLOCK_REF_KEY ||
 	    type == BTRFS_SHARED_DATA_REF_KEY ||
 	    type == BTRFS_EXTENT_DATA_REF_KEY) {
 		if (is_data == BTRFS_REF_TYPE_BLOCK) {
-			if (type == BTRFS_TREE_BLOCK_REF_KEY ||
-			    type == BTRFS_SHARED_BLOCK_REF_KEY)
+			if (type == BTRFS_TREE_BLOCK_REF_KEY)
 				return type;
+			if (type == BTRFS_SHARED_BLOCK_REF_KEY) {
+				ASSERT(eb->fs_info);
+				/*
+				 * Every shared one has parent tree
+				 * block, which must be aligned to
+				 * nodesize.
+				 */
+				if (offset &&
+				    IS_ALIGNED(offset, eb->fs_info->nodesize))
+					return type;
+			}
 		} else if (is_data == BTRFS_REF_TYPE_DATA) {
-			if (type == BTRFS_EXTENT_DATA_REF_KEY ||
-			    type == BTRFS_SHARED_DATA_REF_KEY)
+			if (type == BTRFS_EXTENT_DATA_REF_KEY)
 				return type;
+			if (type == BTRFS_SHARED_DATA_REF_KEY) {
+				ASSERT(eb->fs_info);
+				/*
+				 * Every shared one has parent tree
+				 * block, which must be aligned to
+				 * nodesize.
+				 */
+				if (offset &&
+				    IS_ALIGNED(offset, eb->fs_info->nodesize))
+					return type;
+			}
 		} else {
 			ASSERT(is_data == BTRFS_REF_TYPE_ANY);
 			return type;
diff --git a/fs/btrfs/print-tree.c b/fs/btrfs/print-tree.c
index 0e52e47..9f8c5ee 100644
--- a/fs/btrfs/print-tree.c
+++ b/fs/btrfs/print-tree.c
@@ -44,7 +44,7 @@ static void print_dev_item(struct extent_buffer *eb,
 static void print_extent_data_ref(struct extent_buffer *eb,
 				  struct btrfs_extent_data_ref *ref)
 {
-	pr_info("\t\textent data backref root %llu objectid %llu offset %llu count %u\n",
+	pr_cont("extent data backref root %llu objectid %llu offset %llu count %u\n",
 	       btrfs_extent_data_ref_root(eb, ref),
 	       btrfs_extent_data_ref_objectid(eb, ref),
 	       btrfs_extent_data_ref_offset(eb, ref),
@@ -63,6 +63,7 @@ static void print_extent_item(struct extent_buffer *eb, int slot, int type)
 	u32 item_size = btrfs_item_size_nr(eb, slot);
 	u64 flags;
 	u64 offset;
+	int ref_index = 0;
 
 	if (item_size < sizeof(*ei)) {
 #ifdef BTRFS_COMPAT_EXTENT_TREE_V0
@@ -104,12 +105,20 @@ static void print_extent_item(struct extent_buffer *eb, int slot, int type)
 		iref = (struct btrfs_extent_inline_ref *)ptr;
 		type = btrfs_extent_inline_ref_type(eb, iref);
 		offset = btrfs_extent_inline_ref_offset(eb, iref);
+		pr_info("\t\tref#%d: ", ref_index++);
 		switch (type) {
 		case BTRFS_TREE_BLOCK_REF_KEY:
-			pr_info("\t\ttree block backref root %llu\n", offset);
+			pr_cont("tree block backref root %llu\n", offset);
 			break;
 		case BTRFS_SHARED_BLOCK_REF_KEY:
-			pr_info("\t\tshared block backref parent %llu\n", offset);
+			pr_cont("shared block backref parent %llu\n", offset);
+			/*
+			 * offset is supposed to be a tree block which
+			 * must be aligned to nodesize.
+			 */
+			if (!IS_ALIGNED(offset, eb->fs_info->nodesize))
+				pr_info("\t\t\t(parent %llu is NOT ALIGNED to nodesize %llu)\n",
+					offset, (unsigned long long)eb->fs_info->nodesize);
 			break;
 		case BTRFS_EXTENT_DATA_REF_KEY:
 			dref = (struct btrfs_extent_data_ref *)(&iref->offset);
@@ -117,12 +126,18 @@ static void print_extent_item(struct extent_buffer *eb, int slot, int type)
 			break;
 		case BTRFS_SHARED_DATA_REF_KEY:
 			sref = (struct btrfs_shared_data_ref *)(iref + 1);
-			pr_info("\t\tshared data backref parent %llu count %u\n",
+			pr_cont("shared data backref parent %llu count %u\n",
 			       offset, btrfs_shared_data_ref_count(eb, sref));
+			/*
+			 * offset is supposed to be a tree block which
+			 * must be aligned to nodesize.
+			 */
+			if (!IS_ALIGNED(offset, eb->fs_info->nodesize))
+				pr_info("\t\t\t(parent %llu is NOT ALIGNED to nodesize %llu)\n",
+				     offset, (unsigned long long)eb->fs_info->nodesize);
 			break;
 		default:
-			btrfs_err(eb->fs_info,
-				  "extent %llu has invalid ref type %d\n",
+			pr_cont("(extent %llu has INVALID ref type %d)\n",
 				  eb->start, type);
 			return;
 		}
-- 
2.9.4


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 1/7] Btrfs: add a helper to retrive extent inline ref type
  2017-08-18 21:15 ` [PATCH v3 1/7] Btrfs: add a helper to retrive " Liu Bo
@ 2017-08-21 14:38   ` David Sterba
  0 siblings, 0 replies; 16+ messages in thread
From: David Sterba @ 2017-08-21 14:38 UTC (permalink / raw)
  To: Liu Bo; +Cc: linux-btrfs, David Sterba

On Fri, Aug 18, 2017 at 03:15:18PM -0600, Liu Bo wrote:
> An invalid value of extent inline ref type may be read from a
> malicious image which may force btrfs to crash.
> 
> This adds a helper which does sanity check for the ref type, so we can
> know if it's sane, return type if so, otherwise return an error.
> 
> Signed-off-by: Liu Bo <bo.li.liu@oracle.com>

Reviewed-by: David Sterba <dsterba@suse.com>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 2/7] Btrfs: convert to use btrfs_get_extent_inline_ref_type
  2017-08-18 21:15 ` [PATCH v3 2/7] Btrfs: convert to use btrfs_get_extent_inline_ref_type Liu Bo
@ 2017-08-21 14:52   ` David Sterba
  0 siblings, 0 replies; 16+ messages in thread
From: David Sterba @ 2017-08-21 14:52 UTC (permalink / raw)
  To: Liu Bo; +Cc: linux-btrfs, David Sterba

On Fri, Aug 18, 2017 at 03:15:19PM -0600, Liu Bo wrote:
> Since we have a helper which can do sanity check, this converts all
> btrfs_extent_inline_ref_type to it.
> 
> Signed-off-by: Liu Bo <bo.li.liu@oracle.com>

Reviewed-by: David Sterba <dsterba@suse.com>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 3/7] Btrfs: remove BUG() in btrfs_extent_inline_ref_size
  2017-08-18 21:15 ` [PATCH v3 3/7] Btrfs: remove BUG() in btrfs_extent_inline_ref_size Liu Bo
@ 2017-08-21 14:52   ` David Sterba
  0 siblings, 0 replies; 16+ messages in thread
From: David Sterba @ 2017-08-21 14:52 UTC (permalink / raw)
  To: Liu Bo; +Cc: linux-btrfs, David Sterba

On Fri, Aug 18, 2017 at 03:15:20PM -0600, Liu Bo wrote:
> Now that btrfs_get_extent_inline_ref_type() can report if type is a
> valid one and all callers can gracefully deal with that, we don't need
> to crash here.
> 
> Signed-off-by: Liu Bo <bo.li.liu@oracle.com>

Reviewed-by: David Sterba <dsterba@suse.com>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 4/7] Btrfs: remove BUG() in print_extent_item
  2017-08-18 21:15 ` [PATCH v3 4/7] Btrfs: remove BUG() in print_extent_item Liu Bo
@ 2017-08-21 14:53   ` David Sterba
  0 siblings, 0 replies; 16+ messages in thread
From: David Sterba @ 2017-08-21 14:53 UTC (permalink / raw)
  To: Liu Bo; +Cc: linux-btrfs, David Sterba

On Fri, Aug 18, 2017 at 03:15:21PM -0600, Liu Bo wrote:
> btrfs_print_leaf() is used in btrfs_get_extent_inline_ref_type, so
> here we really want to print the invalid value of ref type instead of
> causing a kernel panic.
> 
> Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
> ---
>  fs/btrfs/print-tree.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/btrfs/print-tree.c b/fs/btrfs/print-tree.c
> index fcae61e..0e52e47 100644
> --- a/fs/btrfs/print-tree.c
> +++ b/fs/btrfs/print-tree.c
> @@ -121,7 +121,10 @@ static void print_extent_item(struct extent_buffer *eb, int slot, int type)
>  			       offset, btrfs_shared_data_ref_count(eb, sref));
>  			break;
>  		default:
> -			BUG();
> +			btrfs_err(eb->fs_info,
> +				  "extent %llu has invalid ref type %d\n",

I'll remove the trailing \n

Reviewed-by: David Sterba <dsterba@suse.com>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 5/7] Btrfs: remove BUG() in add_data_reference
  2017-08-18 21:15 ` [PATCH v3 5/7] Btrfs: remove BUG() in add_data_reference Liu Bo
@ 2017-08-21 14:53   ` David Sterba
  0 siblings, 0 replies; 16+ messages in thread
From: David Sterba @ 2017-08-21 14:53 UTC (permalink / raw)
  To: Liu Bo; +Cc: linux-btrfs, David Sterba

On Fri, Aug 18, 2017 at 03:15:22PM -0600, Liu Bo wrote:
> Now that we have a helper to report invalid value of extent inline ref
> type, we need to quit gracefully instead of throwing out a kernel panic.
> 
> Signed-off-by: Liu Bo <bo.li.liu@oracle.com>

Reviewed-by: David Sterba <dsterba@suse.com>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 6/7] Btrfs: remove BUG_ON in __add_tree_block
  2017-08-18 21:15 ` [PATCH v3 6/7] Btrfs: remove BUG_ON in __add_tree_block Liu Bo
@ 2017-08-21 14:59   ` David Sterba
  0 siblings, 0 replies; 16+ messages in thread
From: David Sterba @ 2017-08-21 14:59 UTC (permalink / raw)
  To: Liu Bo; +Cc: linux-btrfs, David Sterba

On Fri, Aug 18, 2017 at 03:15:23PM -0600, Liu Bo wrote:
> The BUG_ON() can be triggered when the caller is processing an invalid
> extent inline ref, e.g.
> 
> a shared data ref is offered instead of a extent data ref, such that
> it tries to find a non-exist tree block and then btrfs_search_slot
> returns 1 for no such item.
> 
> This replaces the BUG_ON() with a WARN() followed by calling
> btrfs_print_leaf() to show more details about what's going on and
> returning -EINVAL to upper callers.
> 
> Signed-off-by: Liu Bo <bo.li.liu@oracle.com>

Reviewed-by: David Sterba <dsterba@suse.com>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 7/7] Btrfs: add one more sanity check for shared ref type
  2017-08-18 21:15 ` [PATCH v3 7/7] Btrfs: add one more sanity check for shared ref type Liu Bo
@ 2017-08-21 15:00   ` David Sterba
  0 siblings, 0 replies; 16+ messages in thread
From: David Sterba @ 2017-08-21 15:00 UTC (permalink / raw)
  To: Liu Bo; +Cc: linux-btrfs, David Sterba

On Fri, Aug 18, 2017 at 03:15:24PM -0600, Liu Bo wrote:
> Every shared ref has a parent tree block, which can be get from
> btrfs_extent_inline_ref_offset().  And the tree block must be aligned
> to the nodesize, so we'd know this inline ref is not valid if this
> block's bytenr is not aligned to the nodesize, in which case, most
> likely the ref type has been misused.
> 
> This adds the above mentioned check and also updates
> print_extent_item() called by btrfs_print_leaf() to point out the
> invalid ref while printing the tree structure.
> 
> Signed-off-by: Liu Bo <bo.li.liu@oracle.com>

Reviewed-by: David Sterba <dsterba@suse.com>

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 0/7] add sanity check for extent inline ref type
  2017-08-18 21:15 [PATCH v3 0/7] add sanity check for extent inline ref type Liu Bo
                   ` (6 preceding siblings ...)
  2017-08-18 21:15 ` [PATCH v3 7/7] Btrfs: add one more sanity check for shared ref type Liu Bo
@ 2017-08-21 15:32 ` David Sterba
  7 siblings, 0 replies; 16+ messages in thread
From: David Sterba @ 2017-08-21 15:32 UTC (permalink / raw)
  To: Liu Bo; +Cc: linux-btrfs, David Sterba

On Fri, Aug 18, 2017 at 03:15:17PM -0600, Liu Bo wrote:
> An invalid extent inline ref type could be read from a btrfs image and
> it ends up with a panic[1], this set is to deal with the insane value
> gracefully in patch 1-2 and clean up BUG() in the code in patch 3-6.
> 
> Patch 7 adds one more check to see if the ref is a valid shared one.
> 
> I'm not sure in the real world what may result in this corruption, but
> I've seen several reports on the ML about __btrfs_free_extent saying
> something was missing (or simply wrong), while testing this set with
> btrfs-corrupt-block, I found that switching ref type could end up that
> situation as well, eg. a data extent's ref type
> (BTRFS_EXTENT_DATA_REF_KEY) is switched to (BTRFS_TREE_BLOCK_REF_KEY).
> Hopefully this can give people more sights next time when that
> happens.
> 
> [1]:https://www.spinics.net/lists/linux-btrfs/msg65646.html
> 
> v3:
> - btrfs_inline_ref_types -> btrfs_inline_ref_type
> - convert WARN(1) to btrfs_err so that we know which btrfs has that error.
> 
> v2:
> - add enum type and return BTRFS_REF_TYPE_INVALID instead of -EINVAL.
> - remove one more BUG_ON which is in __add_tree_block.
> - add validation check for shared refs.
> - improve btrfs_print_leaf to show which refs has something wrong.
> 
> Liu Bo (7):
>   Btrfs: add a helper to retrive extent inline ref type
>   Btrfs: convert to use btrfs_get_extent_inline_ref_type
>   Btrfs: remove BUG() in btrfs_extent_inline_ref_size
>   Btrfs: remove BUG() in print_extent_item
>   Btrfs: remove BUG() in add_data_reference
>   Btrfs: remove BUG_ON in __add_tree_block
>   Btrfs: add one more sanity check for shared ref type

Added to 4.14 queue, some trivial fixups were needed due to other patches.

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2017-08-21 15:33 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-08-18 21:15 [PATCH v3 0/7] add sanity check for extent inline ref type Liu Bo
2017-08-18 21:15 ` [PATCH v3 1/7] Btrfs: add a helper to retrive " Liu Bo
2017-08-21 14:38   ` David Sterba
2017-08-18 21:15 ` [PATCH v3 2/7] Btrfs: convert to use btrfs_get_extent_inline_ref_type Liu Bo
2017-08-21 14:52   ` David Sterba
2017-08-18 21:15 ` [PATCH v3 3/7] Btrfs: remove BUG() in btrfs_extent_inline_ref_size Liu Bo
2017-08-21 14:52   ` David Sterba
2017-08-18 21:15 ` [PATCH v3 4/7] Btrfs: remove BUG() in print_extent_item Liu Bo
2017-08-21 14:53   ` David Sterba
2017-08-18 21:15 ` [PATCH v3 5/7] Btrfs: remove BUG() in add_data_reference Liu Bo
2017-08-21 14:53   ` David Sterba
2017-08-18 21:15 ` [PATCH v3 6/7] Btrfs: remove BUG_ON in __add_tree_block Liu Bo
2017-08-21 14:59   ` David Sterba
2017-08-18 21:15 ` [PATCH v3 7/7] Btrfs: add one more sanity check for shared ref type Liu Bo
2017-08-21 15:00   ` David Sterba
2017-08-21 15:32 ` [PATCH v3 0/7] add sanity check for extent inline " David Sterba

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.