Commit c871b0f2 authored by Liu Bo's avatar Liu Bo Committed by David Sterba

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: default avatarLiu Bo <bo.li.liu@oracle.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 16ff4b45
...@@ -2512,6 +2512,8 @@ read_block_for_search(struct btrfs_trans_handle *trans, ...@@ -2512,6 +2512,8 @@ read_block_for_search(struct btrfs_trans_handle *trans,
if (!btrfs_buffer_uptodate(tmp, 0, 0)) if (!btrfs_buffer_uptodate(tmp, 0, 0))
ret = -EIO; ret = -EIO;
free_extent_buffer(tmp); free_extent_buffer(tmp);
} else {
ret = PTR_ERR(tmp);
} }
return ret; return ret;
} }
......
...@@ -1098,7 +1098,7 @@ void readahead_tree_block(struct btrfs_root *root, u64 bytenr) ...@@ -1098,7 +1098,7 @@ void readahead_tree_block(struct btrfs_root *root, u64 bytenr)
struct inode *btree_inode = root->fs_info->btree_inode; struct inode *btree_inode = root->fs_info->btree_inode;
buf = btrfs_find_create_tree_block(root, bytenr); buf = btrfs_find_create_tree_block(root, bytenr);
if (!buf) if (IS_ERR(buf))
return; return;
read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree, read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree,
buf, 0, WAIT_NONE, btree_get_extent, 0); buf, 0, WAIT_NONE, btree_get_extent, 0);
...@@ -1114,7 +1114,7 @@ int reada_tree_block_flagged(struct btrfs_root *root, u64 bytenr, ...@@ -1114,7 +1114,7 @@ int reada_tree_block_flagged(struct btrfs_root *root, u64 bytenr,
int ret; int ret;
buf = btrfs_find_create_tree_block(root, bytenr); buf = btrfs_find_create_tree_block(root, bytenr);
if (!buf) if (IS_ERR(buf))
return 0; return 0;
set_bit(EXTENT_BUFFER_READAHEAD, &buf->bflags); set_bit(EXTENT_BUFFER_READAHEAD, &buf->bflags);
...@@ -1172,8 +1172,8 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr, ...@@ -1172,8 +1172,8 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
int ret; int ret;
buf = btrfs_find_create_tree_block(root, bytenr); buf = btrfs_find_create_tree_block(root, bytenr);
if (!buf) if (IS_ERR(buf))
return ERR_PTR(-ENOMEM); return buf;
ret = btree_read_extent_buffer_pages(root, buf, 0, parent_transid); ret = btree_read_extent_buffer_pages(root, buf, 0, parent_transid);
if (ret) { if (ret) {
......
...@@ -8016,8 +8016,9 @@ btrfs_init_new_buffer(struct btrfs_trans_handle *trans, struct btrfs_root *root, ...@@ -8016,8 +8016,9 @@ btrfs_init_new_buffer(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct extent_buffer *buf; struct extent_buffer *buf;
buf = btrfs_find_create_tree_block(root, bytenr); buf = btrfs_find_create_tree_block(root, bytenr);
if (!buf) if (IS_ERR(buf))
return ERR_PTR(-ENOMEM); return buf;
btrfs_set_header_generation(buf, trans->transid); btrfs_set_header_generation(buf, trans->transid);
btrfs_set_buffer_lockdep_class(root->root_key.objectid, buf, level); btrfs_set_buffer_lockdep_class(root->root_key.objectid, buf, level);
btrfs_tree_lock(buf); btrfs_tree_lock(buf);
...@@ -8659,8 +8660,9 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, ...@@ -8659,8 +8660,9 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans,
next = btrfs_find_tree_block(root->fs_info, bytenr); next = btrfs_find_tree_block(root->fs_info, bytenr);
if (!next) { if (!next) {
next = btrfs_find_create_tree_block(root, bytenr); next = btrfs_find_create_tree_block(root, bytenr);
if (!next) if (IS_ERR(next))
return -ENOMEM; return PTR_ERR(next);
btrfs_set_buffer_lockdep_class(root->root_key.objectid, next, btrfs_set_buffer_lockdep_class(root->root_key.objectid, next,
level - 1); level - 1);
reada = 1; reada = 1;
......
...@@ -4892,18 +4892,25 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info, ...@@ -4892,18 +4892,25 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info,
int uptodate = 1; int uptodate = 1;
int ret; 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); eb = find_extent_buffer(fs_info, start);
if (eb) if (eb)
return eb; return eb;
eb = __alloc_extent_buffer(fs_info, start, len); eb = __alloc_extent_buffer(fs_info, start, len);
if (!eb) if (!eb)
return NULL; return ERR_PTR(-ENOMEM);
for (i = 0; i < num_pages; i++, index++) { for (i = 0; i < num_pages; i++, index++) {
p = find_or_create_page(mapping, index, GFP_NOFS|__GFP_NOFAIL); p = find_or_create_page(mapping, index, GFP_NOFS|__GFP_NOFAIL);
if (!p) if (!p) {
exists = ERR_PTR(-ENOMEM);
goto free_eb; goto free_eb;
}
spin_lock(&mapping->private_lock); spin_lock(&mapping->private_lock);
if (PagePrivate(p)) { if (PagePrivate(p)) {
...@@ -4948,8 +4955,10 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info, ...@@ -4948,8 +4955,10 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info,
set_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags); set_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags);
again: again:
ret = radix_tree_preload(GFP_NOFS); ret = radix_tree_preload(GFP_NOFS);
if (ret) if (ret) {
exists = ERR_PTR(ret);
goto free_eb; goto free_eb;
}
spin_lock(&fs_info->buffer_lock); spin_lock(&fs_info->buffer_lock);
ret = radix_tree_insert(&fs_info->buffer_radix, ret = radix_tree_insert(&fs_info->buffer_radix,
......
...@@ -2422,8 +2422,8 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans, ...@@ -2422,8 +2422,8 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
root_owner = btrfs_header_owner(parent); root_owner = btrfs_header_owner(parent);
next = btrfs_find_create_tree_block(root, bytenr); next = btrfs_find_create_tree_block(root, bytenr);
if (!next) if (IS_ERR(next))
return -ENOMEM; return PTR_ERR(next);
if (*level == 1) { if (*level == 1) {
ret = wc->process_func(root, next, wc, ptr_gen); ret = wc->process_func(root, next, wc, ptr_gen);
......
...@@ -6607,8 +6607,8 @@ int btrfs_read_sys_array(struct btrfs_root *root) ...@@ -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. * 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); sb = btrfs_find_create_tree_block(root, BTRFS_SUPER_INFO_OFFSET);
if (!sb) if (IS_ERR(sb))
return -ENOMEM; return PTR_ERR(sb);
set_extent_buffer_uptodate(sb); set_extent_buffer_uptodate(sb);
btrfs_set_buffer_lockdep_class(root->root_key.objectid, sb, 0); btrfs_set_buffer_lockdep_class(root->root_key.objectid, sb, 0);
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment