WARNING in ext4_journalled_invalidate_folio

Status: upstream: reported C repro on 2026/11/12 04:57
Subsystems: ext4
Labels: prio:low
[Documentation on labels]
Reported-by: syzbot+f111db4cb2acd44eb2f4@syzkaller.appspotmail.com
First crash: 8h28m, last: 12m
✨ AI-generated root cause summary:

When a buffered write fails partially (e.g. short copy due to user page fault) on an ext4 inode in data=journal mode with blocksize < PAGE_SIZE, ext4_journalled_write_end() invokes ext4_truncate_failed_write() to discard blocks allocated past inode->i_size. In data=journal mode, buffers attached to a folio straddling inode->i_size that are undergoing transaction commit cannot be invalidated immediately by jbd2_journal_invalidate_folio() and return -EBUSY. While the normal truncate path (ext4_truncate_down()) calls ext4_wait_for_tail_page_commit() to wait for the committing transaction before truncating the page cache, ext4_truncate_failed_write() directly calls truncate_inode_pages(), causing ext4_journalled_invalidate_folio() to encounter -EBUSY and trigger a WARN_ON.

▶ ▼ ✨ Full AI root cause analysis
Based on kernel commit 067c5c24cef2:

Root Cause Analysis

1. Relevant Functions, Structs, and Files

  • Files:
    • fs/ext4/truncate.h: Defines ext4_truncate_failed_write().
    • fs/ext4/inode.c: Defines ext4_journalled_write_end(), ext4_journalled_invalidate_folio(), __ext4_journalled_invalidate_folio(), and ext4_wait_for_tail_page_commit().
    • fs/jbd2/transaction.c: Defines jbd2_journal_invalidate_folio() and journal_unmap_buffer().
    • mm/truncate.c: Implements truncate_inode_pages_range(), truncate_inode_partial_folio(), and folio_invalidate().
  • Structures and Fields:
    • struct folio: Folio straddling EOF / inode->i_size.
    • struct buffer_head & struct journal_head: Buffer and journal metadata heads attached to the folio.
    • journal_t: JBD2 journal (journal->j_committing_transaction, journal->j_running_transaction).
    • transaction_t: Current committing transaction (jh->b_transaction == journal->j_committing_transaction).
    • struct address_space_operations: ext4_journalled_aops.invalidate_folio.

2. Triggering Mechanism and Invariant Violation

  1. Triggering Sequence:

    • A file on an ext4 filesystem configured in data=journal mode (ext4_should_journal_data(inode) == true) with a sub-page block size (blocksize < PAGE_SIZE, e.g., 1KB blocks with 4KB pages) undergoes an extending buffered write (pwritev -> generic_perform_write()).
    • ext4_write_begin() instantiates and allocates new disk blocks beyond the existing inode->i_size.
    • The user-space copy operation then experiences a fault or short copy (copied < len, e.g., page fault at CR2 = 0x20000000d000).
    • Execution reaches ext4_journalled_write_end() (fs/ext4/inode.c):
      • The journalled buffers are zeroed and marked dirty via write_end_fn() -> ext4_dirty_journalled_data().
      • The inode size is updated to pos + copied via ext4_update_inode_size(), leaving inode->i_size unaligned with the page boundary (straddling the folio).
      • The inode is added to the orphan list (ext4_orphan_add()), and the transaction handle is stopped via ext4_journal_stop(handle).
      • Because blocks were allocated past the new inode->i_size (pos + len > inode->i_size), ext4_journalled_write_end() calls ext4_truncate_failed_write(inode) to clean up unwritten blocks.
  2. The Invariant Violation:

    • In JBD2, when invalidating a partial folio (partial_page == (offset || length < folio_size(folio))), any buffer_head undergoing commit (jh->b_transaction == journal->j_committing_transaction) cannot be discarded or unmapped immediately because its contents are actively being written to the journal.
    • journal_unmap_buffer() (fs/jbd2/transaction.c:2406-2415) explicitly enforces this rule:
      if (partial_page) {
          ...
          return -EBUSY;
      }
      
      Callers invalidating partial folios in data=journal mode must wait for the committing transaction to complete and then retry.
    • Because VFS a_ops->invalidate_folio() returns void, the page cache truncation code (truncate_inode_partial_folio()) cannot handle or retry -EBUSY.
    • In the standard truncate path (ext4_truncate_down(), fs/ext4/inode.c:6974), ext4 explicitly satisfies this requirement before calling truncate_pagecache():
      if (ext4_should_journal_data(inode))
          ext4_wait_for_tail_page_commit(inode);
      
    • However, ext4_truncate_failed_write() (fs/ext4/truncate.h:12-24) lacks this check:
      static inline void ext4_truncate_failed_write(struct inode *inode)
      {
          struct address_space *mapping = inode->i_mapping;
      
          filemap_invalidate_lock(mapping);
          truncate_inode_pages(mapping, inode->i_size);
          ext4_truncate(inode);
          filemap_invalidate_unlock(mapping);
      }
      
      It directly calls truncate_inode_pages(mapping, inode->i_size) without calling ext4_wait_for_tail_page_commit(inode).
  3. Manifestation:

    • truncate_inode_pages() calls truncate_inode_partial_folio(), which calls folio_invalidate(folio, offset, length), invoking ext4_journalled_invalidate_folio().
    • __ext4_journalled_invalidate_folio() calls jbd2_journal_invalidate_folio().
    • Because the buffer is still tied to journal->j_committing_transaction and partial_page is true, journal_unmap_buffer() returns -EBUSY (RBX = 0xfffffff0).
    • ext4_journalled_invalidate_folio() checks the return code and encounters -EBUSY < 0, triggering the warning at line 3561:
      WARN_ON(__ext4_journalled_invalidate_folio(folio, offset, length) < 0);
      
▶ ▼ ✨ AI Jobs (1)
ID Workflow Result Correct Bug Created Started Finished Revision
3d0d86ea assessment-security DenialOfService: ✅ Exploitable: ❌ FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: ❌ NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ❌ RemoteTrigger: ❌ Unprivileged: ❌ UserNamespace: ❌ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ WARNING in ext4_journalled_invalidate_folio 2026/11/11 21:19 2026/11/11 21:19 2026/11/11 22:01 8d68b303
▶ ▼ Discussions (1)
Title Replies (including bot) Last reply
[syzbot] [ext4?] WARNING in ext4_journalled_invalidate_folio 0 (1) 2026/11/12 04:57

Sample crash report:
------------[ cut here ]------------
WARNING: fs/ext4/inode.c:3561 at ext4_journalled_invalidate_folio+0x64/0x6c fs/ext4/inode.c:3561, CPU#1: syz.3.128/5701
Modules linked in:
CPU: 1 UID: 0 PID: 5701 Comm: syz.3.128 Not tainted syzkaller #0 PREEMPT 
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 11/06/2026
pstate: 83400005 (Nzcv daif +PAN -UAO +TCO +DIT -SSBS BTYPE=--)
pc : ext4_journalled_invalidate_folio+0x64/0x6c fs/ext4/inode.c:3561
lr : ext4_journalled_invalidate_folio+0x64/0x6c fs/ext4/inode.c:3561
sp : ffff800099227130
x29: ffff800099227130 x28: 1fffffbff87922c3 x27: ffff800086b75f98
x26: fffffdffc3c91618 x25: 00000000000009c5 x24: 000000000000063b
x23: 0000000000001000 x22: fffffdffc3c91608 x21: fffffdffc3c91600
x20: 000000000000063b x19: 00000000fffffff0 x18: ffff80012364b000
x17: 0000000000000000 x16: 0000000000009800 x15: 0000000000000000
x14: 00000000006b0ec7 x13: 0000000000000001 x12: 0000000000000000
x11: 0000000000000000 x10: ffff800089889200 x9 : 0000000000000000
x8 : 0000000000000000 x7 : ffff8000811b24f8 x6 : 0000000000000000
x5 : 0000000000000000 x4 : 0000000000000001 x3 : ffff8000810a36e4
x2 : 00000000fffffff0 x1 : ffff0000c4d31d80 x0 : 0000000000000000
Call trace:
 ext4_journalled_invalidate_folio+0x64/0x6c fs/ext4/inode.c:3561 (P)
 folio_invalidate mm/truncate.c:140 [inline]
 truncate_inode_partial_folio+0x574/0xb38 mm/truncate.c:261
 truncate_inode_pages_range+0x4e8/0xc9c mm/truncate.c:460
 truncate_inode_pages+0x2c/0x3c mm/truncate.c:526
 ext4_truncate_failed_write fs/ext4/truncate.h:21 [inline]
 ext4_journalled_write_end+0x698/0xa60 fs/ext4/inode.c:1726
 generic_perform_write+0x504/0x7a4 mm/filemap.c:4413
 ext4_buffered_write_iter+0xfc/0x438 fs/ext4/file.c:372
 ext4_file_write_iter+0x1f4/0x1478 fs/ext4/file.c:-1
 do_iter_readv_writev+0x48c/0x6f8 fs/read_write.c:-1
 vfs_writev+0x2a8/0x630 fs/read_write.c:1058
 do_pwritev+0x170/0x2a0 fs/read_write.c:1153
 __do_sys_pwritev2 fs/read_write.c:1212 [inline]
 __se_sys_pwritev2 fs/read_write.c:1203 [inline]
 __arm64_sys_pwritev2+0xe4/0x118 fs/read_write.c:1203
 __invoke_syscall arch/arm64/kernel/syscall.c:35 [inline]
 invoke_syscall+0x98/0x244 arch/arm64/kernel/syscall.c:49
 el0_svc_common+0xec/0x23c arch/arm64/kernel/syscall.c:121
 do_el0_svc+0x4c/0x5c arch/arm64/kernel/syscall.c:140
 el0_svc+0x60/0x1d8 arch/arm64/kernel/entry-common.c:797
 el0t_64_sync_handler+0x44/0x104 arch/arm64/kernel/entry-common.c:816
 el0t_64_sync+0x198/0x19c arch/arm64/kernel/entry.S:606
irq event stamp: 25834
hardirqs last  enabled at (25833): [<ffff800080bff1cc>] kasan_quarantine_put+0xb4/0x1c4 mm/kasan/quarantine.c:234
hardirqs last disabled at (25834): [<ffff800086953878>] el1_brk64+0x20/0x54 arch/arm64/kernel/entry-common.c:484
softirqs last  enabled at (25130): [<ffff8000800204f4>] __do_softirq+0x14/0x20 kernel/softirq.c:703
softirqs last disabled at (24937): [<ffff8000800204f4>] __do_softirq+0x14/0x20 kernel/softirq.c:703
---[ end trace 0000000000000000 ]---

Crashes (11):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2026/11/12 04:56 git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git for-kernelci 58c1eb84e977 8d68b303 .config console log report syz / log C [disk image] [vmlinux] [kernel image] [mounted in repro (clean fs)] ci-upstream-gce-arm64 WARNING in ext4_journalled_invalidate_folio
2026/11/12 05:36 upstream 79eb6679ef6a 8d68b303 .config console log report [disk image (non-bootable)] [vmlinux] [kernel image] ci-snapshot-upstream-root WARNING in ext4_journalled_invalidate_folio
2026/11/12 05:36 upstream 79eb6679ef6a 8d68b303 .config console log report [disk image (non-bootable)] [vmlinux] [kernel image] ci-snapshot-upstream-root WARNING in ext4_journalled_invalidate_folio
2026/11/12 04:50 upstream 79eb6679ef6a 8d68b303 .config console log report [disk image (non-bootable)] [vmlinux] [kernel image] ci-snapshot-upstream-root WARNING in ext4_journalled_invalidate_folio
2026/11/12 02:55 upstream 79eb6679ef6a 8d68b303 .config console log report [disk image (non-bootable)] [vmlinux] [kernel image] ci-snapshot-upstream-root WARNING in ext4_journalled_invalidate_folio
2026/11/11 21:26 upstream 067c5c24cef2 e56a968c .config console log report [disk image (non-bootable)] [vmlinux] [kernel image] ci-snapshot-upstream-root WARNING in ext4_journalled_invalidate_folio
2026/11/11 21:25 upstream 067c5c24cef2 e56a968c .config console log report [disk image (non-bootable)] [vmlinux] [kernel image] ci-snapshot-upstream-root WARNING in ext4_journalled_invalidate_folio
2026/11/11 21:23 upstream 067c5c24cef2 e56a968c .config console log report [disk image (non-bootable)] [vmlinux] [kernel image] ci-snapshot-upstream-root WARNING in ext4_journalled_invalidate_folio
2026/11/11 21:22 upstream 067c5c24cef2 e56a968c .config console log report [disk image (non-bootable)] [vmlinux] [kernel image] ci-snapshot-upstream-root WARNING in ext4_journalled_invalidate_folio
2026/11/11 21:19 upstream 067c5c24cef2 e56a968c .config console log report [disk image (non-bootable)] [vmlinux] [kernel image] ci-snapshot-upstream-root WARNING in ext4_journalled_invalidate_folio
2026/11/12 02:50 git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git for-kernelci 58c1eb84e977 8d68b303 .config console log report info [disk image] [vmlinux] [kernel image] ci-upstream-gce-arm64 WARNING in ext4_journalled_invalidate_folio
* Struck through repros no longer work on HEAD.