--- /dev/null
+From 1d3ad183943b38eec2acf72a0ae98e635dc8456b Mon Sep 17 00:00:00 2001
+From: Deepanshu Kartikey <kartikey406@gmail.com>
+Date: Tue, 30 Sep 2025 16:58:10 +0530
+Subject: ext4: detect invalid INLINE_DATA + EXTENTS flag combination
+
+From: Deepanshu Kartikey <kartikey406@gmail.com>
+
+commit 1d3ad183943b38eec2acf72a0ae98e635dc8456b upstream.
+
+syzbot reported a BUG_ON in ext4_es_cache_extent() when opening a verity
+file on a corrupted ext4 filesystem mounted without a journal.
+
+The issue is that the filesystem has an inode with both the INLINE_DATA
+and EXTENTS flags set:
+
+ EXT4-fs error (device loop0): ext4_cache_extents:545: inode #15:
+ comm syz.0.17: corrupted extent tree: lblk 0 < prev 66
+
+Investigation revealed that the inode has both flags set:
+ DEBUG: inode 15 - flag=1, i_inline_off=164, has_inline=1, extents_flag=1
+
+This is an invalid combination since an inode should have either:
+- INLINE_DATA: data stored directly in the inode
+- EXTENTS: data stored in extent-mapped blocks
+
+Having both flags causes ext4_has_inline_data() to return true, skipping
+extent tree validation in __ext4_iget(). The unvalidated out-of-order
+extents then trigger a BUG_ON in ext4_es_cache_extent() due to integer
+underflow when calculating hole sizes.
+
+Fix this by detecting this invalid flag combination early in ext4_iget()
+and rejecting the corrupted inode.
+
+Cc: stable@kernel.org
+Reported-and-tested-by: syzbot+038b7bf43423e132b308@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=038b7bf43423e132b308
+Suggested-by: Zhang Yi <yi.zhang@huawei.com>
+Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
+Reviewed-by: Zhang Yi <yi.zhang@huawei.com>
+Message-ID: <20250930112810.315095-1-kartikey406@gmail.com>
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/ext4/inode.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/fs/ext4/inode.c
++++ b/fs/ext4/inode.c
+@@ -4868,6 +4868,14 @@ struct inode *__ext4_iget(struct super_b
+ }
+ ei->i_flags = le32_to_cpu(raw_inode->i_flags);
+ ext4_set_inode_flags(inode, true);
++ /* Detect invalid flag combination - can't have both inline data and extents */
++ if (ext4_test_inode_flag(inode, EXT4_INODE_INLINE_DATA) &&
++ ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) {
++ ext4_error_inode(inode, function, line, 0,
++ "inode has both inline data and extents flags");
++ ret = -EFSCORRUPTED;
++ goto bad_inode;
++ }
+ inode->i_blocks = ext4_inode_blocks(raw_inode, ei);
+ ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl_lo);
+ if (ext4_has_feature_64bit(sb))
--- /dev/null
+From 3c652c3a71de1d30d72dc82c3bead8deb48eb749 Mon Sep 17 00:00:00 2001
+From: Zhang Yi <yi.zhang@huawei.com>
+Date: Tue, 16 Sep 2025 17:33:36 +0800
+Subject: jbd2: ensure that all ongoing I/O complete before freeing blocks
+
+From: Zhang Yi <yi.zhang@huawei.com>
+
+commit 3c652c3a71de1d30d72dc82c3bead8deb48eb749 upstream.
+
+When releasing file system metadata blocks in jbd2_journal_forget(), if
+this buffer has not yet been checkpointed, it may have already been
+written back, currently be in the process of being written back, or has
+not yet written back. jbd2_journal_forget() calls
+jbd2_journal_try_remove_checkpoint() to check the buffer's status and
+add it to the current transaction if it has not been written back. This
+buffer can only be reallocated after the transaction is committed.
+
+jbd2_journal_try_remove_checkpoint() attempts to lock the buffer and
+check its dirty status while holding the buffer lock. If the buffer has
+already been written back, everything proceeds normally. However, there
+are two issues. First, the function returns immediately if the buffer is
+locked by the write-back process. It does not wait for the write-back to
+complete. Consequently, until the current transaction is committed and
+the block is reallocated, there is no guarantee that the I/O will
+complete. This means that ongoing I/O could write stale metadata to the
+newly allocated block, potentially corrupting data. Second, the function
+unlocks the buffer as soon as it detects that the buffer is still dirty.
+If a concurrent write-back occurs immediately after this unlocking and
+before clear_buffer_dirty() is called in jbd2_journal_forget(), data
+corruption can theoretically still occur.
+
+Although these two issues are unlikely to occur in practice since the
+undergoing metadata writeback I/O does not take this long to complete,
+it's better to explicitly ensure that all ongoing I/O operations are
+completed.
+
+Fixes: 597599268e3b ("jbd2: discard dirty data when forgetting an un-journalled buffer")
+Cc: stable@kernel.org
+Suggested-by: Jan Kara <jack@suse.cz>
+Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
+Reviewed-by: Jan Kara <jack@suse.cz>
+Message-ID: <20250916093337.3161016-2-yi.zhang@huaweicloud.com>
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/jbd2/transaction.c | 13 +++++++++----
+ 1 file changed, 9 insertions(+), 4 deletions(-)
+
+--- a/fs/jbd2/transaction.c
++++ b/fs/jbd2/transaction.c
+@@ -1642,6 +1642,7 @@ int jbd2_journal_forget(handle_t *handle
+ int drop_reserve = 0;
+ int err = 0;
+ int was_modified = 0;
++ int wait_for_writeback = 0;
+
+ if (is_handle_aborted(handle))
+ return -EROFS;
+@@ -1766,18 +1767,22 @@ int jbd2_journal_forget(handle_t *handle
+ }
+
+ /*
+- * The buffer is still not written to disk, we should
+- * attach this buffer to current transaction so that the
+- * buffer can be checkpointed only after the current
+- * transaction commits.
++ * The buffer has not yet been written to disk. We should
++ * either clear the buffer or ensure that the ongoing I/O
++ * is completed, and attach this buffer to current
++ * transaction so that the buffer can be checkpointed only
++ * after the current transaction commits.
+ */
+ clear_buffer_dirty(bh);
++ wait_for_writeback = 1;
+ __jbd2_journal_file_buffer(jh, transaction, BJ_Forget);
+ spin_unlock(&journal->j_list_lock);
+ }
+ drop:
+ __brelse(bh);
+ spin_unlock(&jh->b_state_lock);
++ if (wait_for_writeback)
++ wait_on_buffer(bh);
+ jbd2_journal_put_journal_head(jh);
+ if (drop_reserve) {
+ /* no need to reserve log space for this block -bzzz */