evict() can race with writeback_sb_inodes() and so list_empty(&inode->i_io_list) check can race with list_move() from redirty_tail() possibly resulting in list_empty() returning false and thus we end up leaving freed inode in wb->b_dirty list leading to use-after-free issues. Fix the problem by using list_empty_careful() check and add assert that inode's i_io_list is empty in clear_inode() to catch the problem earlier in the future. Signed-off-by: Jan Kara <jack@xxxxxxx> --- fs/inode.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/inode.c b/fs/inode.c index 93d9252a00ab..a73c8a7aa71a 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -534,6 +534,7 @@ void clear_inode(struct inode *inode) BUG_ON(!(inode->i_state & I_FREEING)); BUG_ON(inode->i_state & I_CLEAR); BUG_ON(!list_empty(&inode->i_wb_list)); + BUG_ON(!list_empty(&inode->i_io_list)); /* don't need i_lock here, no concurrent mods to i_state */ inode->i_state = I_FREEING | I_CLEAR; } @@ -559,7 +560,13 @@ static void evict(struct inode *inode) BUG_ON(!(inode->i_state & I_FREEING)); BUG_ON(!list_empty(&inode->i_lru)); - if (!list_empty(&inode->i_io_list)) + /* + * We are the only holder of the inode so it cannot be marked dirty. + * Flusher thread won't start new writeback but there can be still e.g. + * redirty_tail() running from writeback_sb_inodes(). So we have to be + * careful to remove inode from dirty/io list in all the cases. + */ + if (!list_empty_careful(&inode->i_io_list)) inode_io_list_del(inode); inode_sb_list_del(inode); -- 2.16.4