| From 1846430c24d66e85cc58286b3319c82cd54debb2 Mon Sep 17 00:00:00 2001 |
| From: Liu Bo <bo.li.liu@oracle.com> |
| Date: Thu, 25 Jan 2018 11:02:51 -0700 |
| Subject: Btrfs: fix crash due to not cleaning up tree log block's dirty bits |
| |
| From: Liu Bo <bo.li.liu@oracle.com> |
| |
| commit 1846430c24d66e85cc58286b3319c82cd54debb2 upstream. |
| |
| In cases that the whole fs flips into readonly status due to failures in |
| critical sections, then log tree's blocks are still dirty, and this leads |
| to a crash during umount time, the crash is about use-after-free, |
| |
| umount |
| -> close_ctree |
| -> stop workers |
| -> iput(btree_inode) |
| -> iput_final |
| -> write_inode_now |
| -> ... |
| -> queue job on stop'd workers |
| |
| cc: <stable@vger.kernel.org> v3.12+ |
| Fixes: 681ae50917df ("Btrfs: cleanup reserved space when freeing tree log on error") |
| Signed-off-by: Liu Bo <bo.li.liu@oracle.com> |
| Reviewed-by: Josef Bacik <jbacik@fb.com> |
| Signed-off-by: David Sterba <dsterba@suse.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/btrfs/tree-log.c | 9 +++++++++ |
| 1 file changed, 9 insertions(+) |
| |
| --- a/fs/btrfs/tree-log.c |
| +++ b/fs/btrfs/tree-log.c |
| @@ -2445,6 +2445,9 @@ static noinline int walk_down_log_tree(s |
| next); |
| btrfs_wait_tree_block_writeback(next); |
| btrfs_tree_unlock(next); |
| + } else { |
| + if (test_and_clear_bit(EXTENT_BUFFER_DIRTY, &next->bflags)) |
| + clear_extent_buffer_dirty(next); |
| } |
| |
| WARN_ON(root_owner != |
| @@ -2524,6 +2527,9 @@ static noinline int walk_up_log_tree(str |
| next); |
| btrfs_wait_tree_block_writeback(next); |
| btrfs_tree_unlock(next); |
| + } else { |
| + if (test_and_clear_bit(EXTENT_BUFFER_DIRTY, &next->bflags)) |
| + clear_extent_buffer_dirty(next); |
| } |
| |
| WARN_ON(root_owner != BTRFS_TREE_LOG_OBJECTID); |
| @@ -2600,6 +2606,9 @@ static int walk_log_tree(struct btrfs_tr |
| clean_tree_block(trans, log->fs_info, next); |
| btrfs_wait_tree_block_writeback(next); |
| btrfs_tree_unlock(next); |
| + } else { |
| + if (test_and_clear_bit(EXTENT_BUFFER_DIRTY, &next->bflags)) |
| + clear_extent_buffer_dirty(next); |
| } |
| |
| WARN_ON(log->root_key.objectid != |