fs/ext4/ext4_jbd2.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
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: syzbot+b75d75f957975f3d40e3@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=b75d75f957975f3d40e3
Fixes: 49ef8832fb1a ("bdev: implement freeze and thaw holder operations")
Signed-off-by: Luyao Bai <bailuyao1997@gmail.com>
---
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
On Thu, Jul 23, 2026 at 08:50:36PM +0000, Luyao Bai wrote:
> 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: syzbot+b75d75f957975f3d40e3@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=b75d75f957975f3d40e3
> Fixes: 49ef8832fb1a ("bdev: implement freeze and thaw holder operations")
> Signed-off-by: Luyao Bai <bailuyao1997@gmail.com>
> ---
> 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;
> + }
I can't say whether this is the right fix but it feels hacky and racy
unless this code holds s_umount - which it may well do.
On Fri, Jul 24, 2026 at 10:11:01AM -0500, Christian Brauner wrote: > I can't say whether this is the right fix but it feels hacky and racy > unless this code holds s_umount - which it may well do. Oh, it's worse than that. From the Sashiko review[1]: > [Severity: High] > Does returning -EROFS here cause fsync() to spuriously fail? If userspace > calls fsync() on a file with dirty pages while the filesystem is in > SB_FREEZE_COMPLETE, since fsync() bypasses VFS freeze locks, it seems it > would hit this new path and fail with -EROFS instead of safely blocking > until the freeze is lifted. And it's not just fsync(). Any caller of ext4_journal_start() will end falling into this codepath, which means most ext4 operations happening during a freeze could very well fail with an EROFS error. > Also, is there a TOCTOU race with SB_FREEZE_FS here? If a background > writeback thread evaluates this lockless check while VFS is in the > SB_FREEZE_FS phase, it will evaluate to false and proceed to > jbd2__journal_start() where it blocks on the journal barrier. When > ext4_freeze() finishes, it drops the barrier and VFS sets > SB_FREEZE_COMPLETE. The blocked thread then wakes up and starts a > transaction on the now-frozen filesystem. Luyao, I'm curious what (if any) testing you performed before submitting this patch? And I'm curious if some AI / LLM was used in the preparation of this patch? Thanks, - Ted [1] https://sashiko.dev/#/patchset/20260723205036.661832-1-bailuyao1997@gmail.com?part=1
On Fri, Jul 24, 2026, Theodore Ts'o wrote: > And it's not just fsync(). Any caller of ext4_journal_start() will > end falling into this codepath, which means most ext4 operations > happening during a freeze could very well fail with an EROFS error. Thank you both for the review. The Sashiko findings are correct, and I now believe this patch is wrong; please consider it withdrawn. When I reviewed the change, I took the neighbouring checks as precedent: the sb_rdonly() check just above and the aborted-journal check below both fail with -EROFS, so I assumed the frozen case should be handled the same way. What I missed is that those two are permanent conditions, for which failing is correct, while a freeze is transient: callers that legitimately reach this point while frozen should block until thaw, not get an error. The existing WARN_ON() is diagnostic only and lets the transaction proceed, so replacing it with a hard -EROFS changes the behavior of every journal start during a freeze. The race you point out is also real: the check is lockless, so a task can pass it during SB_FREEZE_FS, block on the journal barrier held by ext4_freeze(), and then start its transaction right after the freeze completes. And the underlying report (dirty pages surviving the freeze when writeback errors are swallowed during sync) looks like it needs to be handled in the VFS/writeback path, not here. > Luyao, I'm curious what (if any) testing you performed before > submitting this patch? I ran the syzbot C reproducer under QEMU on a clean mainline build to confirm the WARNING fires, and again with the patch applied to confirm it no longer does, plus a build of fs/ext4/ and checkpatch. I did not exercise fsync-during-freeze or the freeze/thaw transition (e.g. the fstests freeze tests), which is why the problems above escaped me. > And I'm curious if some AI / LLM was used in > the preparation of this patch? Yes. The patch was prepared with the assistance of LLM-based tooling I am experimenting with for kernel bug fixing; I reviewed the change, built it and ran the reproducer myself, and I take full responsibility for it under the DCO. I should have included an Assisted-by: tag per Documentation/process/coding-assistants.rst, and I apologize for omitting it. Future submissions from me will carry the tag. Thanks again for the careful review, and sorry for the noise. Luyao
On Fri, Jul 24, 2026 at 02:16:59PM -0500, Luyao Bai wrote: > > Luyao, I'm curious what (if any) testing you performed before > > submitting this patch? > > I ran the syzbot C reproducer under QEMU on a clean mainline build to > confirm the WARNING fires, and again with the patch applied to confirm > it no longer does, plus a build of fs/ext4/ and checkpatch. I did not > exercise fsync-during-freeze or the freeze/thaw transition (e.g. the > fstests freeze tests), which is why the problems above escaped me. In general I ask people who submit patches, especially if they are relatively new to ext4 development to run at least the fstests smoke tests, using (for example) "kvm-xfstests smoke" using this test appliance. [1] https://github.com/tytso/xfstests-bld/blob/master/Documentation/kvm-quickstart.md Unfortunately, this wouldn't be sufficient since the smoke tests don't exercise the freeze/thaw transition, since the focus is on keeping smoke tests to 15-20 minutes. I'm *fairly* certain the problem with this patch would have been detected by the auto group (e.g., "kvm-xfstests -c ext4/default -g auto") but that takes 2 to 2.5 hours to run, and it wasn't clear we could get people to run that much testing. An interesting approach if we are using LLM-based tooling is to create some kind of prompt that examines the patch, and the generates a list of tests that exercises the code paths modified by patch. Personally, because I have access to Cloud VM's, I'll just run "gce-xfstests ltm -c ext4/all,ext4/64k -g auto", which takes 24 hours of VM time (sharded across multiple VM's, so it only takes about 2.5 hours worth of wall clock time). And since that's only about $2 of cloud VM time, it hasn't been worth it for me to try to optimize set of tests to run. But if you only have access to using something like kvm-xfstets, maybe it would be worthwhile to optimize the wall clock time of running the tests. Cheers, - Ted
© 2016 - 2026 Red Hat, Inc.