]>
Commit | Line | Data |
---|---|---|
21f93658 GKH |
1 | From 537f38f019fa0b762dbb4c0fc95d7fcce9db8e2d Mon Sep 17 00:00:00 2001 |
2 | From: Nikolay Borisov <nborisov@suse.com> | |
3 | Date: Thu, 14 Mar 2019 09:52:35 +0200 | |
4 | Subject: btrfs: Correctly free extent buffer in case btree_read_extent_buffer_pages fails | |
5 | ||
6 | From: Nikolay Borisov <nborisov@suse.com> | |
7 | ||
8 | commit 537f38f019fa0b762dbb4c0fc95d7fcce9db8e2d upstream. | |
9 | ||
10 | If a an eb fails to be read for whatever reason - it's corrupted on disk | |
11 | and parent transid/key validations fail or IO for eb pages fail then | |
12 | this buffer must be removed from the buffer cache. Currently the code | |
13 | calls free_extent_buffer if an error occurs. Unfortunately this doesn't | |
14 | achieve the desired behavior since btrfs_find_create_tree_block returns | |
15 | with eb->refs == 2. | |
16 | ||
17 | On the other hand free_extent_buffer will only decrement the refs once | |
18 | leaving it added to the buffer cache radix tree. This enables later | |
19 | code to look up the buffer from the cache and utilize it potentially | |
20 | leading to a crash. | |
21 | ||
22 | The correct way to free the buffer is call free_extent_buffer_stale. | |
23 | This function will correctly call atomic_dec explicitly for the buffer | |
24 | and subsequently call release_extent_buffer which will decrement the | |
25 | final reference thus correctly remove the invalid buffer from buffer | |
26 | cache. This change affects only newly allocated buffers since they have | |
27 | eb->refs == 2. | |
28 | ||
29 | Link: https://bugzilla.kernel.org/show_bug.cgi?id=202755 | |
30 | Reported-by: Jungyeon <jungyeon@gatech.edu> | |
31 | CC: stable@vger.kernel.org # 4.4+ | |
32 | Signed-off-by: Nikolay Borisov <nborisov@suse.com> | |
33 | Reviewed-by: David Sterba <dsterba@suse.com> | |
34 | Signed-off-by: David Sterba <dsterba@suse.com> | |
35 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
36 | ||
37 | --- | |
38 | fs/btrfs/disk-io.c | 17 +++++++++++------ | |
39 | 1 file changed, 11 insertions(+), 6 deletions(-) | |
40 | ||
41 | --- a/fs/btrfs/disk-io.c | |
42 | +++ b/fs/btrfs/disk-io.c | |
43 | @@ -1017,13 +1017,18 @@ void readahead_tree_block(struct btrfs_f | |
44 | { | |
45 | struct extent_buffer *buf = NULL; | |
46 | struct inode *btree_inode = fs_info->btree_inode; | |
47 | + int ret; | |
48 | ||
49 | buf = btrfs_find_create_tree_block(fs_info, bytenr); | |
50 | if (IS_ERR(buf)) | |
51 | return; | |
52 | - read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree, | |
53 | - buf, WAIT_NONE, 0); | |
54 | - free_extent_buffer(buf); | |
55 | + | |
56 | + ret = read_extent_buffer_pages(&BTRFS_I(btree_inode)->io_tree, buf, | |
57 | + WAIT_NONE, 0); | |
58 | + if (ret < 0) | |
59 | + free_extent_buffer_stale(buf); | |
60 | + else | |
61 | + free_extent_buffer(buf); | |
62 | } | |
63 | ||
64 | int reada_tree_block_flagged(struct btrfs_fs_info *fs_info, u64 bytenr, | |
65 | @@ -1043,12 +1048,12 @@ int reada_tree_block_flagged(struct btrf | |
66 | ret = read_extent_buffer_pages(io_tree, buf, WAIT_PAGE_LOCK, | |
67 | mirror_num); | |
68 | if (ret) { | |
69 | - free_extent_buffer(buf); | |
70 | + free_extent_buffer_stale(buf); | |
71 | return ret; | |
72 | } | |
73 | ||
74 | if (test_bit(EXTENT_BUFFER_CORRUPT, &buf->bflags)) { | |
75 | - free_extent_buffer(buf); | |
76 | + free_extent_buffer_stale(buf); | |
77 | return -EIO; | |
78 | } else if (extent_buffer_uptodate(buf)) { | |
79 | *eb = buf; | |
80 | @@ -1102,7 +1107,7 @@ struct extent_buffer *read_tree_block(st | |
81 | ret = btree_read_extent_buffer_pages(fs_info, buf, parent_transid, | |
82 | level, first_key); | |
83 | if (ret) { | |
84 | - free_extent_buffer(buf); | |
85 | + free_extent_buffer_stale(buf); | |
86 | return ERR_PTR(ret); | |
87 | } | |
88 | return buf; |