Btrfs: check if extent buffer is aligned to sectorsize
Thanks to fuzz testing, we can pass an invalid bytenr to extent buffer via alloc_extent_buffer(). An unaligned eb can have more pages than it should have, which ends up extent buffer's leak or some corrupted content in extent buffer. This adds a warning to let us quickly know what was happening. Now that alloc_extent_buffer() no more returns NULL, this changes its caller and callers of its caller to match with the new error handling. Signed-off-by: Liu Bo <bo.li.liu@oracle.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
16ff4b454f
commit
c871b0f2fd
|
@ -2512,6 +2512,8 @@ read_block_for_search(struct btrfs_trans_handle *trans,
|
|||
if (!btrfs_buffer_uptodate(tmp, 0, 0))
|
||||
ret = -EIO;
|
||||
free_extent_buffer(tmp);
|
||||
} else {
|
||||
ret = PTR_ERR(tmp);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -1098,7 +1098,7 @@ void readahead_tree_block(struct btrfs_root *root, u64 bytenr)
|
|||
struct inode *btree_inode = root->fs_info->btree_inode;
|
||||
|
||||
buf = btrfs_find_create_tree_block(root, bytenr);
|
||||
if (!buf)
|
||||
if (IS_ERR(buf))
|
||||
return;
|
||||
read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree,
|
||||
buf, 0, WAIT_NONE, btree_get_extent, 0);
|
||||
|
@ -1114,7 +1114,7 @@ int reada_tree_block_flagged(struct btrfs_root *root, u64 bytenr,
|
|||
int ret;
|
||||
|
||||
buf = btrfs_find_create_tree_block(root, bytenr);
|
||||
if (!buf)
|
||||
if (IS_ERR(buf))
|
||||
return 0;
|
||||
|
||||
set_bit(EXTENT_BUFFER_READAHEAD, &buf->bflags);
|
||||
|
@ -1172,8 +1172,8 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
|
|||
int ret;
|
||||
|
||||
buf = btrfs_find_create_tree_block(root, bytenr);
|
||||
if (!buf)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (IS_ERR(buf))
|
||||
return buf;
|
||||
|
||||
ret = btree_read_extent_buffer_pages(root, buf, 0, parent_transid);
|
||||
if (ret) {
|
||||
|
|
|
@ -8016,8 +8016,9 @@ btrfs_init_new_buffer(struct btrfs_trans_handle *trans, struct btrfs_root *root,
|
|||
struct extent_buffer *buf;
|
||||
|
||||
buf = btrfs_find_create_tree_block(root, bytenr);
|
||||
if (!buf)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
if (IS_ERR(buf))
|
||||
return buf;
|
||||
|
||||
btrfs_set_header_generation(buf, trans->transid);
|
||||
btrfs_set_buffer_lockdep_class(root->root_key.objectid, buf, level);
|
||||
btrfs_tree_lock(buf);
|
||||
|
@ -8659,8 +8660,9 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
|
|||
next = btrfs_find_tree_block(root->fs_info, bytenr);
|
||||
if (!next) {
|
||||
next = btrfs_find_create_tree_block(root, bytenr);
|
||||
if (!next)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(next))
|
||||
return PTR_ERR(next);
|
||||
|
||||
btrfs_set_buffer_lockdep_class(root->root_key.objectid, next,
|
||||
level - 1);
|
||||
reada = 1;
|
||||
|
|
|
@ -4892,18 +4892,25 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info,
|
|||
int uptodate = 1;
|
||||
int ret;
|
||||
|
||||
if (!IS_ALIGNED(start, fs_info->tree_root->sectorsize)) {
|
||||
btrfs_err(fs_info, "bad tree block start %llu", start);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
eb = find_extent_buffer(fs_info, start);
|
||||
if (eb)
|
||||
return eb;
|
||||
|
||||
eb = __alloc_extent_buffer(fs_info, start, len);
|
||||
if (!eb)
|
||||
return NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for (i = 0; i < num_pages; i++, index++) {
|
||||
p = find_or_create_page(mapping, index, GFP_NOFS|__GFP_NOFAIL);
|
||||
if (!p)
|
||||
if (!p) {
|
||||
exists = ERR_PTR(-ENOMEM);
|
||||
goto free_eb;
|
||||
}
|
||||
|
||||
spin_lock(&mapping->private_lock);
|
||||
if (PagePrivate(p)) {
|
||||
|
@ -4948,8 +4955,10 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info,
|
|||
set_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
|
||||
again:
|
||||
ret = radix_tree_preload(GFP_NOFS);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
exists = ERR_PTR(ret);
|
||||
goto free_eb;
|
||||
}
|
||||
|
||||
spin_lock(&fs_info->buffer_lock);
|
||||
ret = radix_tree_insert(&fs_info->buffer_radix,
|
||||
|
|
|
@ -2422,8 +2422,8 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
|
|||
root_owner = btrfs_header_owner(parent);
|
||||
|
||||
next = btrfs_find_create_tree_block(root, bytenr);
|
||||
if (!next)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(next))
|
||||
return PTR_ERR(next);
|
||||
|
||||
if (*level == 1) {
|
||||
ret = wc->process_func(root, next, wc, ptr_gen);
|
||||
|
|
|
@ -6607,8 +6607,8 @@ int btrfs_read_sys_array(struct btrfs_root *root)
|
|||
* overallocate but we can keep it as-is, only the first page is used.
|
||||
*/
|
||||
sb = btrfs_find_create_tree_block(root, BTRFS_SUPER_INFO_OFFSET);
|
||||
if (!sb)
|
||||
return -ENOMEM;
|
||||
if (IS_ERR(sb))
|
||||
return PTR_ERR(sb);
|
||||
set_extent_buffer_uptodate(sb);
|
||||
btrfs_set_buffer_lockdep_class(root->root_key.objectid, sb, 0);
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue