diff options
author | Liu Bo <bo.li.liu@oracle.com> | 2016-06-06 15:01:23 -0400 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2016-06-17 12:32:40 -0400 |
commit | c871b0f2fd27e7f9097d507f47de5270f88003b9 (patch) | |
tree | 76635b17aa4948c8a8af7f9da1a3a8225ac2d518 /fs | |
parent | 16ff4b454f1b56e8d89a9075feed0dd6ac510c3d (diff) |
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>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/btrfs/ctree.c | 2 | ||||
-rw-r--r-- | fs/btrfs/disk-io.c | 8 | ||||
-rw-r--r-- | fs/btrfs/extent-tree.c | 10 | ||||
-rw-r--r-- | fs/btrfs/extent_io.c | 15 | ||||
-rw-r--r-- | fs/btrfs/tree-log.c | 4 | ||||
-rw-r--r-- | fs/btrfs/volumes.c | 4 |
6 files changed, 28 insertions, 15 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 46025688f1d0..827c949fa4bc 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c | |||
@@ -2512,6 +2512,8 @@ read_block_for_search(struct btrfs_trans_handle *trans, | |||
2512 | if (!btrfs_buffer_uptodate(tmp, 0, 0)) | 2512 | if (!btrfs_buffer_uptodate(tmp, 0, 0)) |
2513 | ret = -EIO; | 2513 | ret = -EIO; |
2514 | free_extent_buffer(tmp); | 2514 | free_extent_buffer(tmp); |
2515 | } else { | ||
2516 | ret = PTR_ERR(tmp); | ||
2515 | } | 2517 | } |
2516 | return ret; | 2518 | return ret; |
2517 | } | 2519 | } |
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 1142127f6e5e..7b5d5e8efde6 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c | |||
@@ -1098,7 +1098,7 @@ void readahead_tree_block(struct btrfs_root *root, u64 bytenr) | |||
1098 | struct inode *btree_inode = root->fs_info->btree_inode; | 1098 | struct inode *btree_inode = root->fs_info->btree_inode; |
1099 | 1099 | ||
1100 | buf = btrfs_find_create_tree_block(root, bytenr); | 1100 | buf = btrfs_find_create_tree_block(root, bytenr); |
1101 | if (!buf) | 1101 | if (IS_ERR(buf)) |
1102 | return; | 1102 | return; |
1103 | read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree, | 1103 | read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree, |
1104 | buf, 0, WAIT_NONE, btree_get_extent, 0); | 1104 | buf, 0, WAIT_NONE, btree_get_extent, 0); |
@@ -1114,7 +1114,7 @@ int reada_tree_block_flagged(struct btrfs_root *root, u64 bytenr, | |||
1114 | int ret; | 1114 | int ret; |
1115 | 1115 | ||
1116 | buf = btrfs_find_create_tree_block(root, bytenr); | 1116 | buf = btrfs_find_create_tree_block(root, bytenr); |
1117 | if (!buf) | 1117 | if (IS_ERR(buf)) |
1118 | return 0; | 1118 | return 0; |
1119 | 1119 | ||
1120 | set_bit(EXTENT_BUFFER_READAHEAD, &buf->bflags); | 1120 | set_bit(EXTENT_BUFFER_READAHEAD, &buf->bflags); |
@@ -1172,8 +1172,8 @@ struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr, | |||
1172 | int ret; | 1172 | int ret; |
1173 | 1173 | ||
1174 | buf = btrfs_find_create_tree_block(root, bytenr); | 1174 | buf = btrfs_find_create_tree_block(root, bytenr); |
1175 | if (!buf) | 1175 | if (IS_ERR(buf)) |
1176 | return ERR_PTR(-ENOMEM); | 1176 | return buf; |
1177 | 1177 | ||
1178 | ret = btree_read_extent_buffer_pages(root, buf, 0, parent_transid); | 1178 | ret = btree_read_extent_buffer_pages(root, buf, 0, parent_transid); |
1179 | if (ret) { | 1179 | if (ret) { |
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 689d25ac6a68..5439e85c4813 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c | |||
@@ -8016,8 +8016,9 @@ btrfs_init_new_buffer(struct btrfs_trans_handle *trans, struct btrfs_root *root, | |||
8016 | struct extent_buffer *buf; | 8016 | struct extent_buffer *buf; |
8017 | 8017 | ||
8018 | buf = btrfs_find_create_tree_block(root, bytenr); | 8018 | buf = btrfs_find_create_tree_block(root, bytenr); |
8019 | if (!buf) | 8019 | if (IS_ERR(buf)) |
8020 | return ERR_PTR(-ENOMEM); | 8020 | return buf; |
8021 | |||
8021 | btrfs_set_header_generation(buf, trans->transid); | 8022 | btrfs_set_header_generation(buf, trans->transid); |
8022 | btrfs_set_buffer_lockdep_class(root->root_key.objectid, buf, level); | 8023 | btrfs_set_buffer_lockdep_class(root->root_key.objectid, buf, level); |
8023 | btrfs_tree_lock(buf); | 8024 | btrfs_tree_lock(buf); |
@@ -8659,8 +8660,9 @@ static noinline int do_walk_down(struct btrfs_trans_handle *trans, | |||
8659 | next = btrfs_find_tree_block(root->fs_info, bytenr); | 8660 | next = btrfs_find_tree_block(root->fs_info, bytenr); |
8660 | if (!next) { | 8661 | if (!next) { |
8661 | next = btrfs_find_create_tree_block(root, bytenr); | 8662 | next = btrfs_find_create_tree_block(root, bytenr); |
8662 | if (!next) | 8663 | if (IS_ERR(next)) |
8663 | return -ENOMEM; | 8664 | return PTR_ERR(next); |
8665 | |||
8664 | btrfs_set_buffer_lockdep_class(root->root_key.objectid, next, | 8666 | btrfs_set_buffer_lockdep_class(root->root_key.objectid, next, |
8665 | level - 1); | 8667 | level - 1); |
8666 | reada = 1; | 8668 | reada = 1; |
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index a3412d68ad37..aaee3ef55ed8 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c | |||
@@ -4892,18 +4892,25 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info, | |||
4892 | int uptodate = 1; | 4892 | int uptodate = 1; |
4893 | int ret; | 4893 | int ret; |
4894 | 4894 | ||
4895 | if (!IS_ALIGNED(start, fs_info->tree_root->sectorsize)) { | ||
4896 | btrfs_err(fs_info, "bad tree block start %llu", start); | ||
4897 | return ERR_PTR(-EINVAL); | ||
4898 | } | ||
4899 | |||
4895 | eb = find_extent_buffer(fs_info, start); | 4900 | eb = find_extent_buffer(fs_info, start); |
4896 | if (eb) | 4901 | if (eb) |
4897 | return eb; | 4902 | return eb; |
4898 | 4903 | ||
4899 | eb = __alloc_extent_buffer(fs_info, start, len); | 4904 | eb = __alloc_extent_buffer(fs_info, start, len); |
4900 | if (!eb) | 4905 | if (!eb) |
4901 | return NULL; | 4906 | return ERR_PTR(-ENOMEM); |
4902 | 4907 | ||
4903 | for (i = 0; i < num_pages; i++, index++) { | 4908 | for (i = 0; i < num_pages; i++, index++) { |
4904 | p = find_or_create_page(mapping, index, GFP_NOFS|__GFP_NOFAIL); | 4909 | p = find_or_create_page(mapping, index, GFP_NOFS|__GFP_NOFAIL); |
4905 | if (!p) | 4910 | if (!p) { |
4911 | exists = ERR_PTR(-ENOMEM); | ||
4906 | goto free_eb; | 4912 | goto free_eb; |
4913 | } | ||
4907 | 4914 | ||
4908 | spin_lock(&mapping->private_lock); | 4915 | spin_lock(&mapping->private_lock); |
4909 | if (PagePrivate(p)) { | 4916 | if (PagePrivate(p)) { |
@@ -4948,8 +4955,10 @@ struct extent_buffer *alloc_extent_buffer(struct btrfs_fs_info *fs_info, | |||
4948 | set_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags); | 4955 | set_bit(EXTENT_BUFFER_UPTODATE, &eb->bflags); |
4949 | again: | 4956 | again: |
4950 | ret = radix_tree_preload(GFP_NOFS); | 4957 | ret = radix_tree_preload(GFP_NOFS); |
4951 | if (ret) | 4958 | if (ret) { |
4959 | exists = ERR_PTR(ret); | ||
4952 | goto free_eb; | 4960 | goto free_eb; |
4961 | } | ||
4953 | 4962 | ||
4954 | spin_lock(&fs_info->buffer_lock); | 4963 | spin_lock(&fs_info->buffer_lock); |
4955 | ret = radix_tree_insert(&fs_info->buffer_radix, | 4964 | ret = radix_tree_insert(&fs_info->buffer_radix, |
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index aa8fed11f749..8ab1dc64cbba 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c | |||
@@ -2422,8 +2422,8 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans, | |||
2422 | root_owner = btrfs_header_owner(parent); | 2422 | root_owner = btrfs_header_owner(parent); |
2423 | 2423 | ||
2424 | next = btrfs_find_create_tree_block(root, bytenr); | 2424 | next = btrfs_find_create_tree_block(root, bytenr); |
2425 | if (!next) | 2425 | if (IS_ERR(next)) |
2426 | return -ENOMEM; | 2426 | return PTR_ERR(next); |
2427 | 2427 | ||
2428 | if (*level == 1) { | 2428 | if (*level == 1) { |
2429 | ret = wc->process_func(root, next, wc, ptr_gen); | 2429 | ret = wc->process_func(root, next, wc, ptr_gen); |
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index fcbda4341f7d..c3a2900c6030 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c | |||
@@ -6607,8 +6607,8 @@ int btrfs_read_sys_array(struct btrfs_root *root) | |||
6607 | * overallocate but we can keep it as-is, only the first page is used. | 6607 | * overallocate but we can keep it as-is, only the first page is used. |
6608 | */ | 6608 | */ |
6609 | sb = btrfs_find_create_tree_block(root, BTRFS_SUPER_INFO_OFFSET); | 6609 | sb = btrfs_find_create_tree_block(root, BTRFS_SUPER_INFO_OFFSET); |
6610 | if (!sb) | 6610 | if (IS_ERR(sb)) |
6611 | return -ENOMEM; | 6611 | return PTR_ERR(sb); |
6612 | set_extent_buffer_uptodate(sb); | 6612 | set_extent_buffer_uptodate(sb); |
6613 | btrfs_set_buffer_lockdep_class(root->root_key.objectid, sb, 0); | 6613 | btrfs_set_buffer_lockdep_class(root->root_key.objectid, sb, 0); |
6614 | /* | 6614 | /* |