Skip to content

Commit 90f2b79

Browse files
author
Brian Foster
committed
ext4: avoid journaling sb update on error if journal is destroying
JIRA: https://issues.redhat.com/browse/RHEL-93593 CVE: CVE-2025-22113 commit ce2f26e Author: Ojaswin Mujoo <ojaswin@linux.ibm.com> Date: Tue Mar 18 13:22:56 2025 +0530 ext4: avoid journaling sb update on error if journal is destroying Presently we always BUG_ON if trying to start a transaction on a journal marked with JBD2_UNMOUNT, since this should never happen. However, while ltp running stress tests, it was observed that in case of some error handling paths, it is possible for update_super_work to start a transaction after the journal is destroyed eg: (umount) ext4_kill_sb kill_block_super generic_shutdown_super sync_filesystem /* commits all txns */ evict_inodes /* might start a new txn */ ext4_put_super flush_work(&sbi->s_sb_upd_work) /* flush the workqueue */ jbd2_journal_destroy journal_kill_thread journal->j_flags |= JBD2_UNMOUNT; jbd2_journal_commit_transaction jbd2_journal_get_descriptor_buffer jbd2_journal_bmap ext4_journal_bmap ext4_map_blocks ... ext4_inode_error ext4_handle_error schedule_work(&sbi->s_sb_upd_work) /* work queue kicks in */ update_super_work jbd2_journal_start start_this_handle BUG_ON(journal->j_flags & JBD2_UNMOUNT) Hence, introduce a new mount flag to indicate journal is destroying and only do a journaled (and deferred) update of sb if this flag is not set. Otherwise, just fallback to an un-journaled commit. Further, in the journal destroy path, we have the following sequence: 1. Set mount flag indicating journal is destroying 2. force a commit and wait for it 3. flush pending sb updates This sequence is important as it ensures that, after this point, there is no sb update that might be journaled so it is safe to update the sb outside the journal. (To avoid race discussed in 2d01ddc) Also, we don't need a similar check in ext4_grp_locked_error since it is only called from mballoc and AFAICT it would be always valid to schedule work here. Fixes: 2d01ddc ("ext4: save error info to sb through journal if available") Reported-by: Mahesh Kumar <maheshkumar657g@gmail.com> Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://patch.msgid.link/9613c465d6ff00cd315602f99283d5f24018c3f7.1742279837.git.ojaswin@linux.ibm.com Signed-off-by: Theodore Ts'o <tytso@mit.edu> Conflicts: - fs/ext4/ext4.h: 1 hunk modified Assisted-by: Patchpal AI Signed-off-by: Brian Foster <bfoster@redhat.com>
1 parent 69a2f79 commit 90f2b79

File tree

3 files changed

+25
-9
lines changed

3 files changed

+25
-9
lines changed

fs/ext4/ext4.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1834,7 +1834,8 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
18341834
enum {
18351835
EXT4_MF_MNTDIR_SAMPLED,
18361836
EXT4_MF_FS_ABORTED, /* Fatal error detected */
1837-
EXT4_MF_FC_INELIGIBLE /* Fast commit ineligible */
1837+
EXT4_MF_FC_INELIGIBLE, /* Fast commit ineligible */
1838+
EXT4_MF_JOURNAL_DESTROY /* Journal is in process of destroying */
18381839
};
18391840

18401841
static inline void ext4_set_mount_flag(struct super_block *sb, int bit)

fs/ext4/ext4_jbd2.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,21 @@ static inline int ext4_journal_destroy(struct ext4_sb_info *sbi, journal_t *jour
521521
{
522522
int err = 0;
523523

524+
/*
525+
* At this point only two things can be operating on the journal.
526+
* JBD2 thread performing transaction commit and s_sb_upd_work
527+
* issuing sb update through the journal. Once we set
528+
* EXT4_JOURNAL_DESTROY, new ext4_handle_error() calls will not
529+
* queue s_sb_upd_work and ext4_force_commit() makes sure any
530+
* ext4_handle_error() calls from the running transaction commit are
531+
* finished. Hence no new s_sb_upd_work can be queued after we
532+
* flush it here.
533+
*/
534+
ext4_set_mount_flag(sbi->s_sb, EXT4_MF_JOURNAL_DESTROY);
535+
536+
ext4_force_commit(sbi->s_sb);
537+
flush_work(&sbi->s_sb_upd_work);
538+
524539
err = jbd2_journal_destroy(journal);
525540
sbi->s_journal = NULL;
526541

fs/ext4/super.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -704,9 +704,13 @@ static void ext4_handle_error(struct super_block *sb, bool force_ro, int error,
704704
* In case the fs should keep running, we need to writeout
705705
* superblock through the journal. Due to lock ordering
706706
* constraints, it may not be safe to do it right here so we
707-
* defer superblock flushing to a workqueue.
707+
* defer superblock flushing to a workqueue. We just need to be
708+
* careful when the journal is already shutting down. If we get
709+
* here in that case, just update the sb directly as the last
710+
* transaction won't commit anyway.
708711
*/
709-
if (continue_fs && journal)
712+
if (continue_fs && journal &&
713+
!ext4_test_mount_flag(sb, EXT4_MF_JOURNAL_DESTROY))
710714
schedule_work(&EXT4_SB(sb)->s_sb_upd_work);
711715
else
712716
ext4_commit_super(sb);
@@ -1236,7 +1240,6 @@ static void ext4_put_super(struct super_block *sb)
12361240
ext4_unregister_li_request(sb);
12371241
ext4_quota_off_umount(sb);
12381242

1239-
flush_work(&sbi->s_sb_upd_work);
12401243
destroy_workqueue(sbi->rsv_conversion_wq);
12411244
ext4_release_orphan_info(sb);
12421245

@@ -1246,7 +1249,8 @@ static void ext4_put_super(struct super_block *sb)
12461249
if ((err < 0) && !aborted) {
12471250
ext4_abort(sb, -err, "Couldn't clean up the journal");
12481251
}
1249-
}
1252+
} else
1253+
flush_work(&sbi->s_sb_upd_work);
12501254

12511255
ext4_es_unregister_shrinker(sbi);
12521256
timer_shutdown_sync(&sbi->s_err_report);
@@ -4906,8 +4910,6 @@ static int ext4_load_and_init_journal(struct super_block *sb,
49064910
return 0;
49074911

49084912
out:
4909-
/* flush s_sb_upd_work before destroying the journal. */
4910-
flush_work(&sbi->s_sb_upd_work);
49114913
ext4_journal_destroy(sbi, sbi->s_journal);
49124914
return -EINVAL;
49134915
}
@@ -5670,8 +5672,6 @@ failed_mount8: __maybe_unused
56705672
sbi->s_ea_block_cache = NULL;
56715673

56725674
if (sbi->s_journal) {
5673-
/* flush s_sb_upd_work before journal destroy. */
5674-
flush_work(&sbi->s_sb_upd_work);
56755675
ext4_journal_destroy(sbi, sbi->s_journal);
56765676
}
56775677
failed_mount3a:

0 commit comments

Comments
 (0)