Skip to content

Commit

Permalink
Btrfs: check if extent buffer is aligned to sectorsize
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Liu Bo authored and David Sterba committed Jun 17, 2016
1 parent 16ff4b4 commit c871b0f
Show file tree
Hide file tree
Showing 6 changed files with 28 additions and 15 deletions.
2 changes: 2 additions & 0 deletions fs/btrfs/ctree.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
8 changes: 4 additions & 4 deletions fs/btrfs/disk-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down
10 changes: 6 additions & 4 deletions fs/btrfs/extent-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
15 changes: 12 additions & 3 deletions fs/btrfs/extent_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions fs/btrfs/tree-log.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions fs/btrfs/volumes.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
/*
Expand Down

0 comments on commit c871b0f

Please sign in to comment.