* [PATCH v2 1/7] Btrfs: add a helper to retrive extent inline ref type
2017-08-07 21:55 [PATCH v2 0/7] add sanity check for extent inline ref type Liu Bo
@ 2017-08-07 21:55 ` Liu Bo
2017-08-07 21:55 ` [PATCH v2 2/7] Btrfs: convert to use btrfs_get_extent_inline_ref_type Liu Bo
` (6 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Liu Bo @ 2017-08-07 21:55 UTC (permalink / raw)
To: linux-btrfs
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.
v2: add enum type and return BTRFS_REF_TYPE_INVALID instead of -EINVAL
Signed-off-by: Liu Bo <bo.li.liu@oracle.com>
---
fs/btrfs/ctree.h | 11 +++++++++++
fs/btrfs/extent-tree.c | 36 ++++++++++++++++++++++++++++++++++++
2 files changed, 47 insertions(+)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 3f3eb7b..9866420 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_types {
+ 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_types 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..83a3cdc 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -1148,6 +1148,42 @@ 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_types 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);
+ WARN(1, "eb %llu invalid extent inline ref type %d\n",
+ eb->start, type);
+
+ 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] 10+ messages in thread* [PATCH v2 2/7] Btrfs: convert to use btrfs_get_extent_inline_ref_type
2017-08-07 21:55 [PATCH v2 0/7] add sanity check for extent inline ref type Liu Bo
2017-08-07 21:55 ` [PATCH v2 1/7] Btrfs: add a helper to retrive " Liu Bo
@ 2017-08-07 21:55 ` Liu Bo
2017-08-07 21:55 ` [PATCH v2 3/7] Btrfs: remove BUG() in btrfs_extent_inline_ref_size Liu Bo
` (5 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Liu Bo @ 2017-08-07 21:55 UTC (permalink / raw)
To: linux-btrfs
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 83a3cdc..80b4db3c 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -1453,12 +1453,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 {
@@ -1619,6 +1625,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;
@@ -1710,6 +1717,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) {
@@ -1717,7 +1729,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) {
@@ -1909,7 +1926,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);
@@ -3194,6 +3216,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;
@@ -3235,8 +3258,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] 10+ messages in thread* [PATCH v2 3/7] Btrfs: remove BUG() in btrfs_extent_inline_ref_size
2017-08-07 21:55 [PATCH v2 0/7] add sanity check for extent inline ref type Liu Bo
2017-08-07 21:55 ` [PATCH v2 1/7] Btrfs: add a helper to retrive " Liu Bo
2017-08-07 21:55 ` [PATCH v2 2/7] Btrfs: convert to use btrfs_get_extent_inline_ref_type Liu Bo
@ 2017-08-07 21:55 ` Liu Bo
2017-08-07 21:55 ` [PATCH v2 4/7] Btrfs: remove BUG() in print_extent_item Liu Bo
` (4 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Liu Bo @ 2017-08-07 21:55 UTC (permalink / raw)
To: linux-btrfs
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 9866420..c915f33 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] 10+ messages in thread* [PATCH v2 4/7] Btrfs: remove BUG() in print_extent_item
2017-08-07 21:55 [PATCH v2 0/7] add sanity check for extent inline ref type Liu Bo
` (2 preceding siblings ...)
2017-08-07 21:55 ` [PATCH v2 3/7] Btrfs: remove BUG() in btrfs_extent_inline_ref_size Liu Bo
@ 2017-08-07 21:55 ` Liu Bo
2017-08-07 21:55 ` [PATCH v2 5/7] Btrfs: remove BUG() in add_data_reference Liu Bo
` (3 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Liu Bo @ 2017-08-07 21:55 UTC (permalink / raw)
To: linux-btrfs
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] 10+ messages in thread* [PATCH v2 5/7] Btrfs: remove BUG() in add_data_reference
2017-08-07 21:55 [PATCH v2 0/7] add sanity check for extent inline ref type Liu Bo
` (3 preceding siblings ...)
2017-08-07 21:55 ` [PATCH v2 4/7] Btrfs: remove BUG() in print_extent_item Liu Bo
@ 2017-08-07 21:55 ` Liu Bo
2017-08-07 21:55 ` [PATCH v2 6/7] Btrfs: remove BUG_ON in __add_tree_block Liu Bo
` (2 subsequent siblings)
7 siblings, 0 replies; 10+ messages in thread
From: Liu Bo @ 2017-08-07 21:55 UTC (permalink / raw)
To: linux-btrfs
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..4806e78 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;
+ WARN(1,
+ "extent %llu slot %d has an invalid inline ref type\n",
+ eb->start, path->slots[0]);
}
if (ret) {
err = ret;
--
2.9.4
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v2 6/7] Btrfs: remove BUG_ON in __add_tree_block
2017-08-07 21:55 [PATCH v2 0/7] add sanity check for extent inline ref type Liu Bo
` (4 preceding siblings ...)
2017-08-07 21:55 ` [PATCH v2 5/7] Btrfs: remove BUG() in add_data_reference Liu Bo
@ 2017-08-07 21:55 ` Liu Bo
2017-08-07 21:55 ` [PATCH v2 7/7] Btrfs: add one more sanity check for shared ref type Liu Bo
2017-08-16 14:53 ` [PATCH v2 0/7] add sanity check for extent inline " David Sterba
7 siblings, 0 replies; 10+ messages in thread
From: Liu Bo @ 2017-08-07 21:55 UTC (permalink / raw)
To: linux-btrfs
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 | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c
index 4806e78..cc6150e 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,15 @@ 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]);
+ WARN(1,
+ "tree block extent item (%llu) is not found in extent tree\n",
+ bytenr);
+ ret = -EINVAL;
+ goto out;
+ }
ret = add_tree_block(rc, &key, path, blocks);
out:
--
2.9.4
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v2 7/7] Btrfs: add one more sanity check for shared ref type
2017-08-07 21:55 [PATCH v2 0/7] add sanity check for extent inline ref type Liu Bo
` (5 preceding siblings ...)
2017-08-07 21:55 ` [PATCH v2 6/7] Btrfs: remove BUG_ON in __add_tree_block Liu Bo
@ 2017-08-07 21:55 ` Liu Bo
2017-08-16 14:53 ` [PATCH v2 0/7] add sanity check for extent inline " David Sterba
7 siblings, 0 replies; 10+ messages in thread
From: Liu Bo @ 2017-08-07 21:55 UTC (permalink / raw)
To: linux-btrfs
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 80b4db3c..61603ec 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_types 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] 10+ messages in thread* Re: [PATCH v2 0/7] add sanity check for extent inline ref type
2017-08-07 21:55 [PATCH v2 0/7] add sanity check for extent inline ref type Liu Bo
` (6 preceding siblings ...)
2017-08-07 21:55 ` [PATCH v2 7/7] Btrfs: add one more sanity check for shared ref type Liu Bo
@ 2017-08-16 14:53 ` David Sterba
2017-08-16 16:04 ` Liu Bo
7 siblings, 1 reply; 10+ messages in thread
From: David Sterba @ 2017-08-16 14:53 UTC (permalink / raw)
To: Liu Bo; +Cc: linux-btrfs
On Mon, Aug 07, 2017 at 03:55:24PM -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
The series looks good to me overall, there are some minor comments. The
use of WARN(1, ...) will lack the common message prefix identifying the
filesystem, so I suggest to use the btrfs_err helper and consider if the
WARN_ON(1) is really useful in the place. Most of them look like that.
in patch btrfs_inline_ref_types, rename it to btrfs_inline_ref_type, so
it's in line with other similar definitions.
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH v2 0/7] add sanity check for extent inline ref type
2017-08-16 14:53 ` [PATCH v2 0/7] add sanity check for extent inline " David Sterba
@ 2017-08-16 16:04 ` Liu Bo
0 siblings, 0 replies; 10+ messages in thread
From: Liu Bo @ 2017-08-16 16:04 UTC (permalink / raw)
To: dsterba, linux-btrfs
On Wed, Aug 16, 2017 at 04:53:15PM +0200, David Sterba wrote:
> On Mon, Aug 07, 2017 at 03:55:24PM -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
>
> The series looks good to me overall, there are some minor comments. The
> use of WARN(1, ...) will lack the common message prefix identifying the
> filesystem, so I suggest to use the btrfs_err helper and consider if the
> WARN_ON(1) is really useful in the place. Most of them look like that.
>
> in patch btrfs_inline_ref_types, rename it to btrfs_inline_ref_type, so
> it's in line with other similar definitions.
Sounds good, I'll update them then.
Thanks,
-liubo
^ permalink raw reply [flat|nested] 10+ messages in thread