aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLiu Bo <bo.li.liu@oracle.com>2016-06-17 22:16:21 -0400
committerChris Mason <clm@fb.com>2016-06-23 13:44:40 -0400
commit415b35a55b57a701afe7391d32a6bb0193b7d3da (patch)
tree2e54dbe6ce61612eb8661107fded18fd656c1289
parent04e1b65af2085d4102b2b5d2fd1e050f8ee63092 (diff)
Btrfs: fix error handling in map_private_extent_buffer
map_private_extent_buffer() can return -EINVAL in two different cases, 1. when the requested contents span two pages if nodesize is larger than pagesize, 2. when it detects something insane. The 2nd one used to be only a WARN_ON(1), and we decided to return a error to callers, but we didn't fix up all its callers, which will be addressed by this patch. Without this, btrfs may end up with 'general protection', ie. reading invalid memory. Reported-by: Vegard Nossum <vegard.nossum@oracle.com> Signed-off-by: Liu Bo <bo.li.liu@oracle.com> Signed-off-by: David Sterba <dsterba@suse.com> Signed-off-by: Chris Mason <clm@fb.com>
-rw-r--r--fs/btrfs/ctree.c6
-rw-r--r--fs/btrfs/extent_io.c7
2 files changed, 11 insertions, 2 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 6276add8538a..a85cf7d23309 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -1786,10 +1786,12 @@ static noinline int generic_bin_search(struct extent_buffer *eb,
1786 if (!err) { 1786 if (!err) {
1787 tmp = (struct btrfs_disk_key *)(kaddr + offset - 1787 tmp = (struct btrfs_disk_key *)(kaddr + offset -
1788 map_start); 1788 map_start);
1789 } else { 1789 } else if (err == 1) {
1790 read_extent_buffer(eb, &unaligned, 1790 read_extent_buffer(eb, &unaligned,
1791 offset, sizeof(unaligned)); 1791 offset, sizeof(unaligned));
1792 tmp = &unaligned; 1792 tmp = &unaligned;
1793 } else {
1794 return err;
1793 } 1795 }
1794 1796
1795 } else { 1797 } else {
@@ -2830,6 +2832,8 @@ cow_done:
2830 } 2832 }
2831 2833
2832 ret = key_search(b, key, level, &prev_cmp, &slot); 2834 ret = key_search(b, key, level, &prev_cmp, &slot);
2835 if (ret < 0)
2836 goto done;
2833 2837
2834 if (level != 0) { 2838 if (level != 0) {
2835 int dec = 0; 2839 int dec = 0;
diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c
index aaee3ef55ed8..75533adef998 100644
--- a/fs/btrfs/extent_io.c
+++ b/fs/btrfs/extent_io.c
@@ -5342,6 +5342,11 @@ int read_extent_buffer_to_user(struct extent_buffer *eb, void __user *dstv,
5342 return ret; 5342 return ret;
5343} 5343}
5344 5344
5345/*
5346 * return 0 if the item is found within a page.
5347 * return 1 if the item spans two pages.
5348 * return -EINVAL otherwise.
5349 */
5345int map_private_extent_buffer(struct extent_buffer *eb, unsigned long start, 5350int map_private_extent_buffer(struct extent_buffer *eb, unsigned long start,
5346 unsigned long min_len, char **map, 5351 unsigned long min_len, char **map,
5347 unsigned long *map_start, 5352 unsigned long *map_start,
@@ -5356,7 +5361,7 @@ int map_private_extent_buffer(struct extent_buffer *eb, unsigned long start,
5356 PAGE_SHIFT; 5361 PAGE_SHIFT;
5357 5362
5358 if (i != end_i) 5363 if (i != end_i)
5359 return -EINVAL; 5364 return 1;
5360 5365
5361 if (i == 0) { 5366 if (i == 0) {
5362 offset = start_offset; 5367 offset = start_offset;