btrfs: tree-checker: Add checker for variable length item
PATCH 1/2 btrfs: tree-checker: Add checker for variable length item
For the following types, we have items with variable length:
(With BTRFS_ prefix and _KEY suffix snipped)
DIR_ITEMDIR_INDEXXATTR_ITEMINODE_REFINODE_EXTREFROOT_REFROOT_BACKREF
They all use @name_len to indicate their name length, and XATTR_ITEM has extra @data_len to indicate it data length.
Despite their variable length, it's also possible to have several such structure inside one item.
This patch will add checker to ensure:
1) No structure header and its data cross item boundary
2) Except XATTR_ITEM, no structure should have non-zero @data_len
This checker is especially useful to avoid possible access beyond boundary for fuzzed image.
--- fs/btrfs/tree-checker.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+)
fs/btrfs/tree-checker.c +123 -0
@@ -222,6 +222,120 @@ static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf,return 0 ; } +static u32 get_header_size (u8 type) +{ +switch (type) { +case BTRFS_DIR_ITEM_KEY : +case BTRFS_DIR_INDEX_KEY : +case BTRFS_XATTR_ITEM_KEY : +return sizeof (struct btrfs_dir_item ); +case BTRFS_INODE_REF_KEY : +return sizeof (struct btrfs_inode_ref ); +case BTRFS_INODE_EXTREF_KEY : +return sizeof (struct btrfs_inode_extref ); +case BTRFS_ROOT_REF_KEY : +case BTRFS_ROOT_BACKREF_KEY : +return sizeof (struct btrfs_root_ref ); + } +WARN_ON (1 ); +return 0 ; +} + +static u16 get_header_namelen (struct extent_buffer *leaf,u8 type, +u32 header_offset) +{ +/* +* @header_offset is offset starts after leaf header, while the +* accessors expects offset starts from leaf header. +* Sowe need to adds LEAF_DATA_OFFSET here +*/ +unsigned long leaf_offset = header_offset +BTRFS_LEAF_DATA_OFFSET ; + +switch (type) { +case BTRFS_DIR_ITEM_KEY : +case BTRFS_DIR_INDEX_KEY : +case BTRFS_XATTR_ITEM_KEY : +return btrfs_dir_name_len (leaf, (void *)leaf_offset); +case BTRFS_INODE_REF_KEY : +return btrfs_inode_ref_name_len (leaf, (void *)leaf_offset); +case BTRFS_INODE_EXTREF_KEY : +return btrfs_inode_extref_name_len (leaf, (void *)leaf_offset); +case BTRFS_ROOT_REF_KEY : +case BTRFS_ROOT_BACKREF_KEY : +return btrfs_root_ref_name_len (leaf, (void *)leaf_offset); + } +WARN_ON (1 ); +return 0 ; +} + +static u16 get_header_datalen (struct extent_buffer *leaf,u8 type, +unsigned long header_offset) +{ +/* Same as get_header_namelen */ +unsigned long leaf_offset = header_offset +BTRFS_LEAF_DATA_OFFSET ; + +switch (type) { +case BTRFS_DIR_ITEM_KEY : +case BTRFS_DIR_INDEX_KEY : +case BTRFS_XATTR_ITEM_KEY : +return btrfs_dir_data_len (leaf, (void *)leaf_offset); + } +return 0 ; +} + +/* +* For items with variable length, normally with namelen and tailing data. +* Like INODE_REF or XATTR +*/ +static int check_variable_length_item (struct btrfs_root *root, +struct extent_buffer *leaf, +struct btrfs_key *key,int slot) +{ +u8 type = key->type; +u32 item_start =btrfs_item_offset_nr (leaf, slot); +u32 item_end =btrfs_item_end_nr (leaf, slot); +u32 header_size =get_header_size (type); +u32 total_size; +u32 cur = item_start; + +while (cur < item_end) { +u32 namelen; +u32 datalen; + +/* header itself should not cross item boundary */ +if (cur + header_size > item_end) { +generic_err (root, leaf, slot, +"structure header crosses item boundary, have %u expect (%u, %u]" , + cur + header_size, cur, item_end); +return -EUCLEAN ; + } + + namelen =get_header_namelen (leaf, type, cur); + datalen =get_header_datalen (leaf, type, cur); + +/* Only XATTR can own data */ +if (type !=BTRFS_XATTR_ITEM_KEY && datalen) { +generic_err (root, leaf, slot, +"item has invalid data len, have %u expect 0" , + datalen); +return -EUCLEAN ; + } + + total_size = header_size + namelen + datalen; + +/* header and name/data should not cross item boundary */ +if (cur + total_size > item_end) { +generic_err (root, leaf, slot, +"structure data crosses item boundary, have %u expect (%u, %u]" , + cur + total_size, cur + header_size, item_end); +return -EUCLEAN ; + } + + cur += total_size; + } +return 0 ; +} +/* * Common point to switch the item-specific validation. */
@@ -238,6 +352,15 @@ static int check_leaf_item(struct btrfs_root *root,case BTRFS_EXTENT_CSUM_KEY : ret =check_csum_item (root, leaf, key, slot);break ; +case BTRFS_DIR_ITEM_KEY : +case BTRFS_XATTR_ITEM_KEY : +case BTRFS_DIR_INDEX_KEY : +case BTRFS_INODE_REF_KEY : +case BTRFS_INODE_EXTREF_KEY : +case BTRFS_ROOT_REF_KEY : +case BTRFS_ROOT_BACKREF_KEY : + ret =check_variable_length_item (root, leaf, key, slot); +break ; }return ret; }
Signature · 1 line
2.14.3
PATCH 2/2 btrfs: Cleanup existing name_len checks
Since tree-checker has verified leaf when reading from disk, we don't need the existing checker.
This cleanup reverts the following commits:fbc326159a01 ("btrfs: Verify dir_item in iterate_object_props") 64c7b01446f4 ("btrfs: Check name_len before in btrfs_del_root_ref") 488d7c456653 ("btrfs: Check name_len before reading btrfs_get_name") 59b0a7f2c7c1 ("btrfs: Check name_len before read in iterate_dir_item") 3c1d41844896 ("btrfs: Check name_len in btrfs_check_ref_name_override") 8ee8c2d62d5f ("btrfs: Verify dir_item in replay_xattr_deletes") 26a836cec2ea ("btrfs: Check name_len on add_inode_ref call path") e79a33270d05 ("btrfs: Check name_len with boundary in verify dir_item") 19c6dcbfa746 ("btrfs: Introduce btrfs_is_name_len_valid to avoid reading beyond boundary")
--- fs/btrfs/ctree.h | 4 +-- fs/btrfs/dir-item.c | 76 ++-------------------------------------------------- fs/btrfs/export.c | 5 ---- fs/btrfs/inode.c | 2 +- fs/btrfs/props.c | 7 ----- fs/btrfs/root-tree.c | 7 ----- fs/btrfs/send.c | 6 ----- fs/btrfs/tree-log.c | 43 ++++++++--------------------- fs/btrfs/xattr.c | 2 +- 9 files changed, 16 insertions(+), 136 deletions(-)
fs/btrfs/ctree.h +1 -3
@@ -3061,14 +3061,12 @@ struct btrfs_dir_item *btrfs_lookup_xattr(struct btrfs_trans_handle *trans, const char *name, u16 name_len, int mod);int verify_dir_item (struct btrfs_fs_info *fs_info, -struct extent_buffer *leaf,int slot, +struct extent_buffer *leaf,struct btrfs_dir_item *dir_item);struct btrfs_dir_item *btrfs_match_dir_item_name (struct btrfs_fs_info *fs_info,struct btrfs_path *path,const char *name,int name_len); -bool btrfs_is_name_len_valid (struct extent_buffer *leaf,int slot, -unsigned long start,u16 name_len);/* orphan.c */ int btrfs_insert_orphan_item(struct btrfs_trans_handle *trans,
fs/btrfs/dir-item.c +2 -74
106 lines not on this page · raw message
fs/btrfs/export.c +0 -5
@@ -282,11 +282,6 @@ static int btrfs_get_name(struct dentry *parent, char *name, name_len =btrfs_inode_ref_name_len (leaf, iref); } - ret =btrfs_is_name_len_valid (leaf, path->slots[0 ], name_ptr, name_len); -if (!ret) { -btrfs_free_path (path); -return -EIO ; - }read_extent_buffer (leaf, name, name_ptr, name_len);btrfs_free_path (path);
fs/btrfs/inode.c +1 -1
@@ -5953,7 +5953,7 @@ static int btrfs_real_readdir(struct file *file, struct dir_context *ctx)if (btrfs_should_delete_dir_index (&del_list, found_key.offset)) goto next; di = btrfs_item_ptr(leaf, slot, struct btrfs_dir_item); -if (verify_dir_item (fs_info, leaf, slot, di)) +if (verify_dir_item (fs_info, leaf, di)) goto next; name_len =btrfs_dir_name_len (leaf, di);
fs/btrfs/props.c +0 -7
@@ -164,7 +164,6 @@ static int iterate_object_props(struct btrfs_root *root, size_t), void *ctx) { -struct btrfs_fs_info *fs_info = root->fs_info;int ret;char *name_buf =NULL ;char *value_buf =NULL ;
@@ -215,12 +214,6 @@ static int iterate_object_props(struct btrfs_root *root, name_ptr = (unsigned long )(di +1 ); data_ptr = name_ptr + name_len; -if (verify_dir_item (fs_info, leaf, - path->slots[0 ], di)) { - ret = -EIO ; - goto out; - } -if (name_len <=XATTR_BTRFS_PREFIX_LEN || memcmp_extent_buffer(leaf, XATTR_BTRFS_PREFIX, name_ptr,
fs/btrfs/root-tree.c +0 -7
@@ -387,13 +387,6 @@ int btrfs_del_root_ref(struct btrfs_trans_handle *trans,WARN_ON (btrfs_root_ref_dirid (leaf, ref) != dirid);WARN_ON (btrfs_root_ref_name_len (leaf, ref) != name_len); ptr = (unsigned long )(ref +1 ); - ret =btrfs_is_name_len_valid (leaf, path->slots[0 ], ptr, - name_len); -if (!ret) { - err = -EIO ; - goto out; - } -WARN_ON (memcmp_extent_buffer (leaf, name, ptr, name_len)); *sequence =btrfs_root_ref_sequence (leaf, ref);
fs/btrfs/send.c +0 -6
@@ -1059,12 +1059,6 @@ static int iterate_dir_item(struct btrfs_root *root, struct btrfs_path *path, } } - ret =btrfs_is_name_len_valid (eb, path->slots[0 ], - (unsigned long )(di +1 ), name_len + data_len); -if (!ret) { - ret = -EIO ; - goto out; - }if (name_len + data_len > buf_len) { buf_len = name_len + data_len;if (is_vmalloc_addr (buf)) {
fs/btrfs/tree-log.c +11 -32
109 lines not on this page · raw message
fs/btrfs/xattr.c +1 -1
@@ -336,7 +336,7 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size)u32 this_len =sizeof (*di) + name_len + data_len;unsigned long name_ptr = (unsigned long )(di +1 ); -if (verify_dir_item (fs_info, leaf, slot, di)) { +if (verify_dir_item (fs_info, leaf, di)) { ret = -EIO ; goto err; }
Signature · 1 line
2.14.3
Re: [PATCH 2/2] btrfs: Cleanup existing name_len checks
Quoted 13 lines · Qu Wenruo
Since tree-checker has verified leaf when reading from disk, we don't need the existing checker.
This cleanup reverts the following commits:
fbc326159a01("btrfs: Verifydir_iteminiterate_object_props")64c7b01446f4("btrfs: Checkname_lenbefore inbtrfs_del_root_ref")488d7c456653("btrfs: Checkname_lenbefore readingbtrfs_get_name")59b0a7f2c7c1("btrfs: Checkname_lenbefore read initerate_dir_item")3c1d41844896("btrfs: Checkname_leninbtrfs_check_ref_name_override")8ee8c2d62d5f("btrfs: Verifydir_iteminreplay_xattr_deletes")26a836cec2ea("btrfs: Checkname_lenonadd_inode_refcall path")e79a33270d05("btrfs: Checkname_lenwith boundary in verifydir_item")19c6dcbfa746("btrfs: Introducebtrfs_is_name_len_validto avoid reading beyond boundary")
Oh well, there it goes, but I like the centralized tree checker more, so this is a small cost.
Re: [PATCH 2/2] btrfs: Cleanup existing name_len checks
Quoted 15 lines · David Sterba
Quoted 13 lines · Qu Wenruo
Since tree-checker has verified leaf when reading from disk, we don't need the existing checker.
This cleanup reverts the following commits:
fbc326159a01("btrfs: Verifydir_iteminiterate_object_props")64c7b01446f4("btrfs: Checkname_lenbefore inbtrfs_del_root_ref")488d7c456653("btrfs: Checkname_lenbefore readingbtrfs_get_name")59b0a7f2c7c1("btrfs: Checkname_lenbefore read initerate_dir_item")3c1d41844896("btrfs: Checkname_leninbtrfs_check_ref_name_override")8ee8c2d62d5f("btrfs: Verifydir_iteminreplay_xattr_deletes")26a836cec2ea("btrfs: Checkname_lenonadd_inode_refcall path")e79a33270d05("btrfs: Checkname_lenwith boundary in verifydir_item")19c6dcbfa746("btrfs: Introducebtrfs_is_name_len_validto avoid reading beyond boundary")
Quoted 4 lines
Oh well, there it goes, but I like the centralized tree checker more, so this is a small cost.
Reviewed-by David Sterba <[email protected]>
Sorry, in v2 patch this patch is also affected.
Since in v2, even verify_dir_item() also get removed, so it's no longer a big revert patch.
Thanks,
Qu
Quoted 5 lines
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 44 lines · Qu Wenruo
For the following types, we have items with variable length:
(WithBTRFS_prefix and_KEYsuffix snipped)
DIR_ITEMDIR_INDEXXATTR_ITEMINODE_REFINODE_EXTREFROOT_REFROOT_BACKREFThey all use @name_len to indicate their name length, and
XATTR_ITEMhas extra @data_len to indicate it data length.Despite their variable length, it's also possible to have several such structure inside one item.
This patch will add checker to ensure:
1) No structure header and its data cross item boundary
2) ExceptXATTR_ITEM, no structure should have non-zero @data_lenThis checker is especially useful to avoid possible access beyond boundary for fuzzed image.
Signed-off-by Qu Wenruo <[email protected]>--- fs/btrfs/tree-checker.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+)fs/btrfs/tree-checker.c +6
-0@@ -222,6 +222,120 @@ static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf,return 0 ; } +static u32 get_header_size (u8 type) +{ +switch (type) { +case BTRFS_DIR_ITEM_KEY : +case BTRFS_DIR_INDEX_KEY : +case BTRFS_XATTR_ITEM_KEY :
fs/btrfs/tree-checker.c static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf, · line 240
Quoted 10 lines
fs/btrfs/tree-checker.c +10
-0+return sizeof (struct btrfs_dir_item ); +case BTRFS_INODE_REF_KEY : +return sizeof (struct btrfs_inode_ref ); +case BTRFS_INODE_EXTREF_KEY : +return sizeof (struct btrfs_inode_extref ); +case BTRFS_ROOT_REF_KEY : +case BTRFS_ROOT_BACKREF_KEY : +return sizeof (struct btrfs_root_ref ); + } +WARN_ON (1 );
The helper name suggests is generic so this warning is there to catch new use, possibly without the matching validation function? If it is so, I'd rather use ASSERT. If you really want a warning, then please replace it with a message. The stacktrace from WARN_ON would not be very useful because we know exactly how we get here, the callchaning starts in the endio handlers.
fs/btrfs/tree-checker.c static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf, · line 251
Quoted 11 lines
fs/btrfs/tree-checker.c +11
-0+return 0 ; +} + +static u16 get_header_namelen (struct extent_buffer *leaf,u8 type, +u32 header_offset) +{ +/* +* @header_offset is offset starts after leaf header, while the +* accessors expects offset starts from leaf header. +* Sowe need to adds LEAF_DATA_OFFSET here +*/
Can you please rephrase the text? (and fix the typos)
Quoted 6 lines
fs/btrfs/tree-checker.c +6
-0+unsigned long leaf_offset = header_offset +BTRFS_LEAF_DATA_OFFSET ; + +switch (type) { +case BTRFS_DIR_ITEM_KEY : +case BTRFS_DIR_INDEX_KEY : +case BTRFS_XATTR_ITEM_KEY :
fs/btrfs/tree-checker.c static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf, · line 267
Quoted 10 lines
fs/btrfs/tree-checker.c +10
-0+return btrfs_dir_name_len (leaf, (void *)leaf_offset); +case BTRFS_INODE_REF_KEY : +return btrfs_inode_ref_name_len (leaf, (void *)leaf_offset); +case BTRFS_INODE_EXTREF_KEY : +return btrfs_inode_extref_name_len (leaf, (void *)leaf_offset); +case BTRFS_ROOT_REF_KEY : +case BTRFS_ROOT_BACKREF_KEY : +return btrfs_root_ref_name_len (leaf, (void *)leaf_offset); + } +WARN_ON (1 );
Similar comment as to the warning above, a message would be more helpful to say which type is unexpected.
Quoted 31 lines
fs/btrfs/tree-checker.c +31
-0+return 0 ; +} + +static u16 get_header_datalen (struct extent_buffer *leaf,u8 type, +unsigned long header_offset) +{ +/* Same as get_header_namelen */ +unsigned long leaf_offset = header_offset +BTRFS_LEAF_DATA_OFFSET ; + +switch (type) { +case BTRFS_DIR_ITEM_KEY : +case BTRFS_DIR_INDEX_KEY : +case BTRFS_XATTR_ITEM_KEY : +return btrfs_dir_data_len (leaf, (void *)leaf_offset); + } +return 0 ; +} + +/* +* For items with variable length, normally with namelen and tailing data. +* Like INODE_REF or XATTR +*/ +static int check_variable_length_item (struct btrfs_root *root, +struct extent_buffer *leaf, +struct btrfs_key *key,int slot) +{ +u8 type = key->type; +u32 item_start =btrfs_item_offset_nr (leaf, slot); +u32 item_end =btrfs_item_end_nr (leaf, slot); +u32 header_size =get_header_size (type); +u32 total_size;
fs/btrfs/tree-checker.c static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf, · line 308
Quoted 10 lines
fs/btrfs/tree-checker.c +10
-0+u32 cur = item_start; + +while (cur < item_end) { +u32 namelen; +u32 datalen; + +/* header itself should not cross item boundary */ +if (cur + header_size > item_end) { + generic_err(root, leaf, slot, + "structure header crosses item boundary, have %u expect (%u, %u]",
Un-indent please. I'm slightly confused by the "(%u, %u]" notation, this reads as an open interval from the left but I don't understand it in this context.
Quoted 11 lines
fs/btrfs/tree-checker.c +11
-0+ cur + header_size, cur, item_end); +return -EUCLEAN ; + } + + namelen =get_header_namelen (leaf, type, cur); + datalen =get_header_datalen (leaf, type, cur); + +/* Only XATTR can own data */ +if (type !=BTRFS_XATTR_ITEM_KEY && datalen) { + generic_err(root, leaf, slot, + "item has invalid data len, have %u expect 0",
fs/btrfs/tree-checker.c static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf, · line 329
Quoted 10 lines
fs/btrfs/tree-checker.c +10
-0+ datalen); +return -EUCLEAN ; + } + + total_size = header_size + namelen + datalen; + +/* header and name/data should not cross item boundary */ +if (cur + total_size > item_end) { + generic_err(root, leaf, slot, + "structure data crosses item boundary, have %u expect (%u, %u]",
Same.
Quoted 18 lines
fs/btrfs/tree-checker.c +11
-0+ cur + total_size, cur + header_size, item_end); +return -EUCLEAN ; + } + + cur += total_size; + } +return 0 ; +} +/* * Common point to switch the item-specific validation. */ @@ -238,6 +352,15 @@ static int check_leaf_item(struct btrfs_root *root,case BTRFS_EXTENT_CSUM_KEY : ret =check_csum_item (root, leaf, key, slot);break ; +case BTRFS_DIR_ITEM_KEY : +case BTRFS_XATTR_ITEM_KEY :
fs/btrfs/tree-checker.c static int check_leaf_item(struct btrfs_root *root, · line 366
Quoted 10 lines
fs/btrfs/tree-checker.c +7
-0+case BTRFS_DIR_INDEX_KEY : +case BTRFS_INODE_REF_KEY : +case BTRFS_INODE_EXTREF_KEY : +case BTRFS_ROOT_REF_KEY : +case BTRFS_ROOT_BACKREF_KEY : + ret =check_variable_length_item (root, leaf, key, slot); +break ; }return ret; }
Otherwise ok.
Quoted 54 lines · David Sterba
Quoted 53 lines · Qu Wenruo
For the following types, we have items with variable length:
(WithBTRFS_prefix and_KEYsuffix snipped)
DIR_ITEMDIR_INDEXXATTR_ITEMINODE_REFINODE_EXTREFROOT_REFROOT_BACKREFThey all use @name_len to indicate their name length, and
XATTR_ITEMhas extra @data_len to indicate it data length.Despite their variable length, it's also possible to have several such structure inside one item.
This patch will add checker to ensure:
1) No structure header and its data cross item boundary
2) ExceptXATTR_ITEM, no structure should have non-zero @data_lenThis checker is especially useful to avoid possible access beyond boundary for fuzzed image.
Signed-off-by Qu Wenruo <[email protected]>--- fs/btrfs/tree-checker.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+)fs/btrfs/tree-checker.c +15
-0@@ -222,6 +222,120 @@ static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf,return 0 ; } +static u32 get_header_size (u8 type) +{ +switch (type) { +case BTRFS_DIR_ITEM_KEY : +case BTRFS_DIR_INDEX_KEY : +case BTRFS_XATTR_ITEM_KEY : +return sizeof (struct btrfs_dir_item ); +case BTRFS_INODE_REF_KEY : +return sizeof (struct btrfs_inode_ref ); +case BTRFS_INODE_EXTREF_KEY : +return sizeof (struct btrfs_inode_extref ); +case BTRFS_ROOT_REF_KEY : +case BTRFS_ROOT_BACKREF_KEY : +return sizeof (struct btrfs_root_ref ); + }
Quoted 8 lines
fs/btrfs/tree-checker.c static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf, · line 240
Quoted 1 line
fs/btrfs/tree-checker.c +1
-0+WARN_ON (1 );The helper name suggests is generic so this warning is there to catch new use, possibly without the matching validation function? If it is so, I'd rather use ASSERT. If you really want a warning, then please replace it with a message. The stacktrace from
WARN_ONwould not be very useful because we know exactly how we get here, the callchaning starts in the endio handlers.
Please ignore this patch, as there is v2 patch which doesn't use such centralized check.
Thanks,
Qu
Quoted 140 lines
fs/btrfs/tree-checker.c static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf, · line 251
Quoted 12 lines
fs/btrfs/tree-checker.c +11
-0+return 0 ; +} + +static u16 get_header_namelen (struct extent_buffer *leaf,u8 type, +u32 header_offset) +{ +/* +* @header_offset is offset starts after leaf header, while the +* accessors expects offset starts from leaf header. +* Sowe need to adds LEAF_DATA_OFFSET here +*/ Can you please rephrase the text? (and fix the typos)
Quoted 6 lines
fs/btrfs/tree-checker.c +6
-0+unsigned long leaf_offset = header_offset +BTRFS_LEAF_DATA_OFFSET ; + +switch (type) { +case BTRFS_DIR_ITEM_KEY : +case BTRFS_DIR_INDEX_KEY : +case BTRFS_XATTR_ITEM_KEY :fs/btrfs/tree-checker.c static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf, · line 267
Quoted 10 lines
fs/btrfs/tree-checker.c +10
-0+return btrfs_dir_name_len (leaf, (void *)leaf_offset); +case BTRFS_INODE_REF_KEY : +return btrfs_inode_ref_name_len (leaf, (void *)leaf_offset); +case BTRFS_INODE_EXTREF_KEY : +return btrfs_inode_extref_name_len (leaf, (void *)leaf_offset); +case BTRFS_ROOT_REF_KEY : +case BTRFS_ROOT_BACKREF_KEY : +return btrfs_root_ref_name_len (leaf, (void *)leaf_offset); + } +WARN_ON (1 );Similar comment as to the warning above, a message would be more helpful to say which type is unexpected.
Quoted 31 lines
fs/btrfs/tree-checker.c +31
-0+return 0 ; +} + +static u16 get_header_datalen (struct extent_buffer *leaf,u8 type, +unsigned long header_offset) +{ +/* Same as get_header_namelen */ +unsigned long leaf_offset = header_offset +BTRFS_LEAF_DATA_OFFSET ; + +switch (type) { +case BTRFS_DIR_ITEM_KEY : +case BTRFS_DIR_INDEX_KEY : +case BTRFS_XATTR_ITEM_KEY : +return btrfs_dir_data_len (leaf, (void *)leaf_offset); + } +return 0 ; +} + +/* +* For items with variable length, normally with namelen and tailing data. +* Like INODE_REF or XATTR +*/ +static int check_variable_length_item (struct btrfs_root *root, +struct extent_buffer *leaf, +struct btrfs_key *key,int slot) +{ +u8 type = key->type; +u32 item_start =btrfs_item_offset_nr (leaf, slot); +u32 item_end =btrfs_item_end_nr (leaf, slot); +u32 header_size =get_header_size (type); +u32 total_size;fs/btrfs/tree-checker.c static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf, · line 308
Quoted 10 lines
fs/btrfs/tree-checker.c +10
-0+u32 cur = item_start; + +while (cur < item_end) { +u32 namelen; +u32 datalen; + +/* header itself should not cross item boundary */ +if (cur + header_size > item_end) { + generic_err(root, leaf, slot, + "structure header crosses item boundary, have %u expect (%u, %u]",Un-indent please. I'm slightly confused by the "(%u, %u]" notation, this reads as an open interval from the left but I don't understand it in this context.
Quoted 11 lines
fs/btrfs/tree-checker.c +11
-0+ cur + header_size, cur, item_end); +return -EUCLEAN ; + } + + namelen =get_header_namelen (leaf, type, cur); + datalen =get_header_datalen (leaf, type, cur); + +/* Only XATTR can own data */ +if (type !=BTRFS_XATTR_ITEM_KEY && datalen) { + generic_err(root, leaf, slot, + "item has invalid data len, have %u expect 0",fs/btrfs/tree-checker.c static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf, · line 329
Quoted 10 lines
fs/btrfs/tree-checker.c +10
-0+ datalen); +return -EUCLEAN ; + } + + total_size = header_size + namelen + datalen; + +/* header and name/data should not cross item boundary */ +if (cur + total_size > item_end) { + generic_err(root, leaf, slot, + "structure data crosses item boundary, have %u expect (%u, %u]",Same.
Quoted 18 lines
fs/btrfs/tree-checker.c +11
-0+ cur + total_size, cur + header_size, item_end); +return -EUCLEAN ; + } + + cur += total_size; + } +return 0 ; +} +/* * Common point to switch the item-specific validation. */ @@ -238,6 +352,15 @@ static int check_leaf_item(struct btrfs_root *root,case BTRFS_EXTENT_CSUM_KEY : ret =check_csum_item (root, leaf, key, slot);break ; +case BTRFS_DIR_ITEM_KEY : +case BTRFS_XATTR_ITEM_KEY :fs/btrfs/tree-checker.c static int check_leaf_item(struct btrfs_root *root, · line 366
Quoted 10 lines
fs/btrfs/tree-checker.c +7
-0+case BTRFS_DIR_INDEX_KEY : +case BTRFS_INODE_REF_KEY : +case BTRFS_INODE_EXTREF_KEY : +case BTRFS_ROOT_REF_KEY : +case BTRFS_ROOT_BACKREF_KEY : + ret =check_variable_length_item (root, leaf, key, slot); +break ; }return ret; }Otherwise ok.
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Quoted 66 lines · Qu Wenruo
Quoted 54 lines · David Sterba
Quoted 53 lines · Qu Wenruo
For the following types, we have items with variable length:
(WithBTRFS_prefix and_KEYsuffix snipped)
DIR_ITEMDIR_INDEXXATTR_ITEMINODE_REFINODE_EXTREFROOT_REFROOT_BACKREFThey all use @name_len to indicate their name length, and
XATTR_ITEMhas extra @data_len to indicate it data length.Despite their variable length, it's also possible to have several such structure inside one item.
This patch will add checker to ensure:
1) No structure header and its data cross item boundary
2) ExceptXATTR_ITEM, no structure should have non-zero @data_lenThis checker is especially useful to avoid possible access beyond boundary for fuzzed image.
Signed-off-by Qu Wenruo <[email protected]>--- fs/btrfs/tree-checker.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+)fs/btrfs/tree-checker.c +15
-0@@ -222,6 +222,120 @@ static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf,return 0 ; } +static u32 get_header_size (u8 type) +{ +switch (type) { +case BTRFS_DIR_ITEM_KEY : +case BTRFS_DIR_INDEX_KEY : +case BTRFS_XATTR_ITEM_KEY : +return sizeof (struct btrfs_dir_item ); +case BTRFS_INODE_REF_KEY : +return sizeof (struct btrfs_inode_ref ); +case BTRFS_INODE_EXTREF_KEY : +return sizeof (struct btrfs_inode_extref ); +case BTRFS_ROOT_REF_KEY : +case BTRFS_ROOT_BACKREF_KEY : +return sizeof (struct btrfs_root_ref ); + }Quoted 8 lines
fs/btrfs/tree-checker.c static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf, · line 240
Quoted 1 line
fs/btrfs/tree-checker.c +1
-0+WARN_ON (1 );The helper name suggests is generic so this warning is there to catch new use, possibly without the matching validation function? If it is so, I'd rather use ASSERT. If you really want a warning, then please replace it with a message. The stacktrace from
WARN_ONwould not be very useful because we know exactly how we get here, the callchaning starts in the endio handlers.
Quoted 2 lines
Please ignore this patch, as there is v2 patch which doesn't use such centralized check.
The already submitted patchset is "[PATCH 0/3] tree-checker bug fix and enhancement."
And patch "[PATCH 2/3] btrfs: tree-checker: Add checker for dir item" was originally going to replace it.
I'll update the patchset to address the indent comment in v2 patchset.
Thanks,
Qu
Quoted 145 lines
Thanks,
QuQuoted 140 lines
fs/btrfs/tree-checker.c static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf, · line 251
Quoted 12 lines
fs/btrfs/tree-checker.c +11
-0+return 0 ; +} + +static u16 get_header_namelen (struct extent_buffer *leaf,u8 type, +u32 header_offset) +{ +/* +* @header_offset is offset starts after leaf header, while the +* accessors expects offset starts from leaf header. +* Sowe need to adds LEAF_DATA_OFFSET here +*/ Can you please rephrase the text? (and fix the typos)
Quoted 6 lines
fs/btrfs/tree-checker.c +6
-0+unsigned long leaf_offset = header_offset +BTRFS_LEAF_DATA_OFFSET ; + +switch (type) { +case BTRFS_DIR_ITEM_KEY : +case BTRFS_DIR_INDEX_KEY : +case BTRFS_XATTR_ITEM_KEY :fs/btrfs/tree-checker.c static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf, · line 267
Quoted 10 lines
fs/btrfs/tree-checker.c +10
-0+return btrfs_dir_name_len (leaf, (void *)leaf_offset); +case BTRFS_INODE_REF_KEY : +return btrfs_inode_ref_name_len (leaf, (void *)leaf_offset); +case BTRFS_INODE_EXTREF_KEY : +return btrfs_inode_extref_name_len (leaf, (void *)leaf_offset); +case BTRFS_ROOT_REF_KEY : +case BTRFS_ROOT_BACKREF_KEY : +return btrfs_root_ref_name_len (leaf, (void *)leaf_offset); + } +WARN_ON (1 );Similar comment as to the warning above, a message would be more helpful to say which type is unexpected.
Quoted 31 lines
fs/btrfs/tree-checker.c +31
-0+return 0 ; +} + +static u16 get_header_datalen (struct extent_buffer *leaf,u8 type, +unsigned long header_offset) +{ +/* Same as get_header_namelen */ +unsigned long leaf_offset = header_offset +BTRFS_LEAF_DATA_OFFSET ; + +switch (type) { +case BTRFS_DIR_ITEM_KEY : +case BTRFS_DIR_INDEX_KEY : +case BTRFS_XATTR_ITEM_KEY : +return btrfs_dir_data_len (leaf, (void *)leaf_offset); + } +return 0 ; +} + +/* +* For items with variable length, normally with namelen and tailing data. +* Like INODE_REF or XATTR +*/ +static int check_variable_length_item (struct btrfs_root *root, +struct extent_buffer *leaf, +struct btrfs_key *key,int slot) +{ +u8 type = key->type; +u32 item_start =btrfs_item_offset_nr (leaf, slot); +u32 item_end =btrfs_item_end_nr (leaf, slot); +u32 header_size =get_header_size (type); +u32 total_size;fs/btrfs/tree-checker.c static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf, · line 308
Quoted 10 lines
fs/btrfs/tree-checker.c +10
-0+u32 cur = item_start; + +while (cur < item_end) { +u32 namelen; +u32 datalen; + +/* header itself should not cross item boundary */ +if (cur + header_size > item_end) { + generic_err(root, leaf, slot, + "structure header crosses item boundary, have %u expect (%u, %u]",Un-indent please. I'm slightly confused by the "(%u, %u]" notation, this reads as an open interval from the left but I don't understand it in this context.
Quoted 11 lines
fs/btrfs/tree-checker.c +11
-0+ cur + header_size, cur, item_end); +return -EUCLEAN ; + } + + namelen =get_header_namelen (leaf, type, cur); + datalen =get_header_datalen (leaf, type, cur); + +/* Only XATTR can own data */ +if (type !=BTRFS_XATTR_ITEM_KEY && datalen) { + generic_err(root, leaf, slot, + "item has invalid data len, have %u expect 0",fs/btrfs/tree-checker.c static int check_csum_item(struct btrfs_root *root, struct extent_buffer *leaf, · line 329
Quoted 10 lines
fs/btrfs/tree-checker.c +10
-0+ datalen); +return -EUCLEAN ; + } + + total_size = header_size + namelen + datalen; + +/* header and name/data should not cross item boundary */ +if (cur + total_size > item_end) { + generic_err(root, leaf, slot, + "structure data crosses item boundary, have %u expect (%u, %u]",Same.
Quoted 18 lines
fs/btrfs/tree-checker.c +11
-0+ cur + total_size, cur + header_size, item_end); +return -EUCLEAN ; + } + + cur += total_size; + } +return 0 ; +} +/* * Common point to switch the item-specific validation. */ @@ -238,6 +352,15 @@ static int check_leaf_item(struct btrfs_root *root,case BTRFS_EXTENT_CSUM_KEY : ret =check_csum_item (root, leaf, key, slot);break ; +case BTRFS_DIR_ITEM_KEY : +case BTRFS_XATTR_ITEM_KEY :fs/btrfs/tree-checker.c static int check_leaf_item(struct btrfs_root *root, · line 366
Quoted 10 lines
fs/btrfs/tree-checker.c +7
-0+case BTRFS_DIR_INDEX_KEY : +case BTRFS_INODE_REF_KEY : +case BTRFS_INODE_EXTREF_KEY : +case BTRFS_ROOT_REF_KEY : +case BTRFS_ROOT_BACKREF_KEY : + ret =check_variable_length_item (root, leaf, key, slot); +break ; }return ret; }Otherwise ok.
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html