aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFilipe Manana <fdmanana@suse.com>2018-10-12 08:02:48 -0400
committerDavid Sterba <dsterba@suse.com>2018-10-19 06:25:33 -0400
commit421f0922a2cfb0c75acd9746454aaa576c711a65 (patch)
treea4c64d9898393ee6aa83016fd4d0c5be423b9a02
parentc495144bc6962186feae31d687596d2472000e45 (diff)
Btrfs: fix use-after-free during inode eviction
At inode.c:evict_inode_truncate_pages(), when we iterate over the inode's extent states, we access an extent state record's "state" field after we unlocked the inode's io tree lock. This can lead to a use-after-free issue because after we unlock the io tree that extent state record might have been freed due to being merged into another adjacent extent state record (a previous inflight bio for a read operation finished in the meanwhile which unlocked a range in the io tree and cause a merge of extent state records, as explained in the comment before the while loop added in commit 6ca0709756710 ("Btrfs: fix hang during inode eviction due to concurrent readahead")). Fix this by keeping a copy of the extent state's flags in a local variable and using it after unlocking the io tree. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=201189 Fixes: b9d0b38928e2 ("btrfs: Add handler for invalidate page") CC: stable@vger.kernel.org # 4.4+ Reviewed-by: Qu Wenruo <wqu@suse.com> Signed-off-by: Filipe Manana <fdmanana@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r--fs/btrfs/inode.c4
1 files changed, 3 insertions, 1 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 28625f344952..f4d31fd62eed 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -5268,11 +5268,13 @@ static void evict_inode_truncate_pages(struct inode *inode)
5268 struct extent_state *cached_state = NULL; 5268 struct extent_state *cached_state = NULL;
5269 u64 start; 5269 u64 start;
5270 u64 end; 5270 u64 end;
5271 unsigned state_flags;
5271 5272
5272 node = rb_first(&io_tree->state); 5273 node = rb_first(&io_tree->state);
5273 state = rb_entry(node, struct extent_state, rb_node); 5274 state = rb_entry(node, struct extent_state, rb_node);
5274 start = state->start; 5275 start = state->start;
5275 end = state->end; 5276 end = state->end;
5277 state_flags = state->state;
5276 spin_unlock(&io_tree->lock); 5278 spin_unlock(&io_tree->lock);
5277 5279
5278 lock_extent_bits(io_tree, start, end, &cached_state); 5280 lock_extent_bits(io_tree, start, end, &cached_state);
@@ -5285,7 +5287,7 @@ static void evict_inode_truncate_pages(struct inode *inode)
5285 * 5287 *
5286 * Note, end is the bytenr of last byte, so we need + 1 here. 5288 * Note, end is the bytenr of last byte, so we need + 1 here.
5287 */ 5289 */
5288 if (state->state & EXTENT_DELALLOC) 5290 if (state_flags & EXTENT_DELALLOC)
5289 btrfs_qgroup_free_data(inode, NULL, start, end - start + 1); 5291 btrfs_qgroup_free_data(inode, NULL, start, end - start + 1);
5290 5292
5291 clear_extent_bit(io_tree, start, end, 5293 clear_extent_bit(io_tree, start, end,