Skip to content

Commit 421f092

Browse files
fdmananakdave
authored andcommitted
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 6ca0709 ("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: b9d0b38 ("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>
1 parent c495144 commit 421f092

File tree

1 file changed

+3
-1
lines changed

1 file changed

+3
-1
lines changed

fs/btrfs/inode.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5268,11 +5268,13 @@ static void evict_inode_truncate_pages(struct inode *inode)
52685268
struct extent_state *cached_state = NULL;
52695269
u64 start;
52705270
u64 end;
5271+
unsigned state_flags;
52715272

52725273
node = rb_first(&io_tree->state);
52735274
state = rb_entry(node, struct extent_state, rb_node);
52745275
start = state->start;
52755276
end = state->end;
5277+
state_flags = state->state;
52765278
spin_unlock(&io_tree->lock);
52775279

52785280
lock_extent_bits(io_tree, start, end, &cached_state);
@@ -5285,7 +5287,7 @@ static void evict_inode_truncate_pages(struct inode *inode)
52855287
*
52865288
* Note, end is the bytenr of last byte, so we need + 1 here.
52875289
*/
5288-
if (state->state & EXTENT_DELALLOC)
5290+
if (state_flags & EXTENT_DELALLOC)
52895291
btrfs_qgroup_free_data(inode, NULL, start, end - start + 1);
52905292

52915293
clear_extent_bit(io_tree, start, end,

0 commit comments

Comments
 (0)