ext4: fix inode use after free in ext4_end_io_rsv_work()
In ext4_io_end_defer_completion(), check if io_end->list_vec is empty to
avoid adding an io_end that requires no conversion to the
i_rsv_conversion_list, which in turn prevents starting an unnecessary
worker. An ext4_emergency_state() check is also added to avoid attempting
to abort the journal in an emergency state.
Additionally, ext4_put_io_end_defer() is refactored to call
ext4_io_end_defer_completion() directly instead of being open-coded.
This also prevents starting an unnecessary worker when EXT4_IO_END_FAILED
is set but data_err=abort is not enabled.
This ensures that the check in ext4_put_io_end_defer() is consistent with
the check in ext4_end_bio(). Otherwise, we might add an io_end to the
i_rsv_conversion_list and then call ext4_finish_bio(), after which the
inode could be freed before ext4_end_io_rsv_work() is called, triggering
a use-after-free issue.
Fixes: ce51afb8cc ("ext4: abort journal on data writeback failure if in data_err=abort mode")
Signed-off-by: Baokun Li <libaokun1@huawei.com>
Reviewed-by: Zhang Yi <yi.zhang@huawei.com>
Reviewed-by: Jan Kara <jack@suse.cz>
Link: https://patch.msgid.link/20250708111504.3208660-1-libaokun@huaweicloud.com
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
This commit is contained in:
+8
-8
@@ -236,10 +236,12 @@ static void dump_completed_IO(struct inode *inode, struct list_head *head)
|
||||
|
||||
static bool ext4_io_end_defer_completion(ext4_io_end_t *io_end)
|
||||
{
|
||||
if (io_end->flag & EXT4_IO_END_UNWRITTEN)
|
||||
if (io_end->flag & EXT4_IO_END_UNWRITTEN &&
|
||||
!list_empty(&io_end->list_vec))
|
||||
return true;
|
||||
if (test_opt(io_end->inode->i_sb, DATA_ERR_ABORT) &&
|
||||
io_end->flag & EXT4_IO_END_FAILED)
|
||||
io_end->flag & EXT4_IO_END_FAILED &&
|
||||
!ext4_emergency_state(io_end->inode->i_sb))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -256,6 +258,7 @@ static void ext4_add_complete_io(ext4_io_end_t *io_end)
|
||||
WARN_ON(!(io_end->flag & EXT4_IO_END_DEFER_COMPLETION));
|
||||
WARN_ON(io_end->flag & EXT4_IO_END_UNWRITTEN &&
|
||||
!io_end->handle && sbi->s_journal);
|
||||
WARN_ON(!io_end->bio);
|
||||
|
||||
spin_lock_irqsave(&ei->i_completed_io_lock, flags);
|
||||
wq = sbi->rsv_conversion_wq;
|
||||
@@ -318,12 +321,9 @@ ext4_io_end_t *ext4_init_io_end(struct inode *inode, gfp_t flags)
|
||||
void ext4_put_io_end_defer(ext4_io_end_t *io_end)
|
||||
{
|
||||
if (refcount_dec_and_test(&io_end->count)) {
|
||||
if (io_end->flag & EXT4_IO_END_FAILED ||
|
||||
(io_end->flag & EXT4_IO_END_UNWRITTEN &&
|
||||
!list_empty(&io_end->list_vec))) {
|
||||
ext4_add_complete_io(io_end);
|
||||
return;
|
||||
}
|
||||
if (ext4_io_end_defer_completion(io_end))
|
||||
return ext4_add_complete_io(io_end);
|
||||
|
||||
ext4_release_io_end(io_end);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user