From a5e0fe753403c64fe14947cdc736fef139f28bd6 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Tue, 21 Apr 2020 10:54:45 +0200 Subject: [PATCH 2686/2944] ext4: Avoid freeing inodes on dirty list fix #34070914 commit ceff86fddae8748fe00d4f2d249cb02cae62ad84 upstream. When we are evicting inode with journalled data, we may race with transaction commit in the following way: CPU0 CPU1 jbd2_journal_commit_transaction() evict(inode) inode_io_list_del() inode_wait_for_writeback() process BJ_Forget list __jbd2_journal_insert_checkpoint() __jbd2_journal_refile_buffer() __jbd2_journal_unfile_buffer() if (test_clear_buffer_jbddirty(bh)) mark_buffer_dirty(bh) __mark_inode_dirty(inode) ext4_evict_inode(inode) frees the inode This results in use-after-free issues in the writeback code (or the assertion added in the previous commit triggering). Fix the problem by removing inode from writeback lists once all the page cache is evicted and so inode cannot be added to writeback lists again. Signed-off-by: Jan Kara Link: https://lore.kernel.org/r/20200421085445.5731-4-jack@suse.cz Signed-off-by: Theodore Ts'o Signed-off-by: Joseph Qi Reviewed-by: Xiaoguang Wang --- fs/ext4/inode.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index ae9c648..b56458f 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -249,6 +249,16 @@ void ext4_evict_inode(struct inode *inode) truncate_inode_pages_final(&inode->i_data); /* + * For inodes with journalled data, transaction commit could have + * dirtied the inode. Flush worker is ignoring it because of I_FREEING + * flag but we still need to remove the inode from the writeback lists. + */ + if (!list_empty_careful(&inode->i_io_list)) { + WARN_ON_ONCE(!ext4_should_journal_data(inode)); + inode_io_list_del(inode); + } + + /* * Protect us against freezing - iput() caller didn't have to have any * protection against it */ -- 1.8.3.1