[PATCH] ext4: do not WARN when starting a journal on a frozen filesystem

Luyao Bai <[email protected]>
Newsgroups org.kernel.vger.linux-ext4,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
A WARNING is triggered in ext4_journal_check_start() when a background
writeback thread attempts to start a journal transaction while the
filesystem is in the SB_FREEZE_COMPLETE state:

  WARNING: CPU: 1 PID: 2903 at fs/ext4/ext4_jbd2.c:76
           ext4_journal_check_start+0x1f8/0x250
  Call Trace:
   __ext4_journal_start_sb+0x181/0x600 fs/ext4/ext4_jbd2.c:105
   __ext4_journal_start fs/ext4/ext4_jbd2.h:326 [inline]
   ext4_do_writepages+0x112c/0x3d20 fs/ext4/inode.c:2707
   ext4_writepages+0x213/0x3c0 fs/ext4/inode.c:2813
   do_writepages+0x35f/0x870 mm/page-writeback.c:2683
   __writeback_single_inode+0x14f/0x10d0 fs/fs-writeback.c:1658
   writeback_sb_inodes+0x80c/0x1370 fs/fs-writeback.c:1954
   wb_writeback+0x41b/0xbd0 fs/fs-writeback.c:2134
   wb_workfn+0x410/0x1090 fs/fs-writeback.c:2321

The background writeback flusher does not take freeze protection, so it
can legitimately reach ext4_do_writepages() and try to start a journal
handle while the filesystem is frozen. This happens, for example, when
ext4_writepages() failed earlier (e.g. -ENOSPC or -EDQUOT) during the
sync_filesystem() phase of the freeze: sync_filesystem() can still
return 0, the freeze completes, but dirty pages are left behind. A later
writeback pass then tries to write them out on the now-frozen
filesystem.

The same state is reachable through the EXT4_IOC_SHUTDOWN path: if
fs_bdev_freeze() succeeds in freezing the superblock but the subsequent
sync_blockdev() fails, the filesystem is left in SB_FREEZE_COMPLETE and
dirty pages remain, so writeback eventually retries and trips the WARN.

Because this state can be reached without any kernel bug, WARN_ON() is
the wrong tool here: WARN_ON() must only fire on conditions that should
never happen. Replace it with an ext4_msg() error message and return
-EROFS, rejecting the transaction cleanly. All callers of
__ext4_journal_start_sb() already handle an error return (it is the same
path used for the existing is_journal_aborted() -EROFS case), so the
dirty pages are simply kept and written back once the filesystem is
thawed.

This patch is intentionally limited to the ext4 journal-start check,
which is the direct cause of the reported warning. Any improvements to
the generic VFS freeze/thaw error handling in fs/super.c are a separate
concern and are deliberately left out of this fix.

Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=b75d75f957975f3d40e3
Fixes: 49ef8832fb1a ("bdev: implement freeze and thaw holder operations")
Signed-off-by: Luyao Bai <[email protected]>
---
Tested with the syzbot reproducer under QEMU: the WARNING at
fs/ext4/ext4_jbd2.c fires on a clean mainline build and no longer fires
with this patch applied.

 fs/ext4/ext4_jbd2.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
index 9a8c225f2..1756039b1 100644
--- a/fs/ext4/ext4_jbd2.c
+++ b/fs/ext4/ext4_jbd2.c
@@ -75,7 +75,12 @@ static int ext4_journal_check_start(struct super_block *sb)
 	if (WARN_ON_ONCE(sb_rdonly(sb)))
 		return -EROFS;

-	WARN_ON(sb->s_writers.frozen == SB_FREEZE_COMPLETE);
+	if (unlikely(sb->s_writers.frozen == SB_FREEZE_COMPLETE)) {
+		ext4_msg(sb, KERN_ERR,
+			 "Attempt to start a journal transaction on a frozen filesystem");
+		return -EROFS;
+	}
+
 	journal = EXT4_SB(sb)->s_journal;
 	/*
 	 * Special case here: if the journal has aborted behind our
--
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.