Commit 415b35a5 authored by Liu Bo's avatar Liu Bo Committed by Chris Mason

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: default avatarVegard Nossum <vegard.nossum@oracle.com>
Signed-off-by: default avatarLiu Bo <bo.li.liu@oracle.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarChris Mason <clm@fb.com>
parent 04e1b65a
...@@ -1786,10 +1786,12 @@ static noinline int generic_bin_search(struct extent_buffer *eb, ...@@ -1786,10 +1786,12 @@ static noinline int generic_bin_search(struct extent_buffer *eb,
if (!err) { if (!err) {
tmp = (struct btrfs_disk_key *)(kaddr + offset - tmp = (struct btrfs_disk_key *)(kaddr + offset -
map_start); map_start);
} else { } else if (err == 1) {
read_extent_buffer(eb, &unaligned, read_extent_buffer(eb, &unaligned,
offset, sizeof(unaligned)); offset, sizeof(unaligned));
tmp = &unaligned; tmp = &unaligned;
} else {
return err;
} }
} else { } else {
...@@ -2830,6 +2832,8 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -2830,6 +2832,8 @@ int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
} }
ret = key_search(b, key, level, &prev_cmp, &slot); ret = key_search(b, key, level, &prev_cmp, &slot);
if (ret < 0)
goto done;
if (level != 0) { if (level != 0) {
int dec = 0; int dec = 0;
......
...@@ -5342,6 +5342,11 @@ int read_extent_buffer_to_user(struct extent_buffer *eb, void __user *dstv, ...@@ -5342,6 +5342,11 @@ int read_extent_buffer_to_user(struct extent_buffer *eb, void __user *dstv,
return ret; return ret;
} }
/*
* return 0 if the item is found within a page.
* return 1 if the item spans two pages.
* return -EINVAL otherwise.
*/
int map_private_extent_buffer(struct extent_buffer *eb, unsigned long start, int map_private_extent_buffer(struct extent_buffer *eb, unsigned long start,
unsigned long min_len, char **map, unsigned long min_len, char **map,
unsigned long *map_start, unsigned long *map_start,
...@@ -5356,7 +5361,7 @@ int map_private_extent_buffer(struct extent_buffer *eb, unsigned long start, ...@@ -5356,7 +5361,7 @@ int map_private_extent_buffer(struct extent_buffer *eb, unsigned long start,
PAGE_SHIFT; PAGE_SHIFT;
if (i != end_i) if (i != end_i)
return -EINVAL; return 1;
if (i == 0) { if (i == 0) {
offset = start_offset; offset = start_offset;
......
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