Re: [PATCH -next v5 31/32] ext4: partially enable iomap for the buffered I/O path of regular files

Zhang Yi <[email protected]>
Newsgroups dev.linux.lists.sashiko-reviews,org.kernel.vger.linux-ext4
Message-ID <[email protected]>
On 8/14/2026 6:39 PM, [email protected] wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [Critical] Encrypted and verity inodes on corrupted or downgraded filesystems bypass fscrypt and fsverity, leading to plaintext data writes and unverified reads.
> - [Medium] Race condition in background writeback causing spurious WARN_ON_ONCE in ext4_iomap_writepages().
> --
> 
> --- Patch [31]: [PATCH -next v5 31/32] ext4: partially enable iomap for the buffered I/O path of regular files ---
> commit 385d0947c4a6c1e02718f1b42a8ef1dc35bdbf03
> Author: Zhang Yi <[email protected]>
> Subject: ext4: partially enable iomap for the buffered I/O path of regular files
> 
> This commit introduces ext4_enable_buffered_iomap() to determine whether a
> regular file inode should use the iomap buffered I/O path. Inodes silently
> fall back to the traditional buffer_head path if unsupported features like
> encryption, verity, inline data, or data=journal mode are met.
> 
>> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
>> index ee15366422a1e..c9ee78fba4d0c 100644
>> --- a/fs/ext4/inode.c
>> +++ b/fs/ext4/inode.c
> 
> [ ... ]
> 
>> @@ -4467,6 +4479,10 @@ static int ext4_iomap_writepages(struct address_space *mapping,
>>   		.ops = &ext4_writeback_ops,
>>   	};
>>   
>> +	/* inode using the buffer_head buffered I/O path should not go here. */
>> +	if (WARN_ON_ONCE(!ext4_inode_buffered_iomap(inode)))
>> +		return -EINVAL;
>> +
> 
> [Severity: Medium]
> Can this WARN_ON_ONCE trigger spuriously due to a race with
> ext4_change_inode_journal_flag()?
> 
> If background writeback via do_writepages() resolves
> mapping->a_ops->writepages to ext4_iomap_writepages locklessly, the thread
> might be preempted right before this check.
> 
> Meanwhile, another thread running ext4_change_inode_journal_flag() could
> flush writeback, acquire s_writepages_rwsem exclusively, and clear the
> EXT4_STATE_BUFFERED_IOMAP flag.
> 
> When the writeback thread resumes, it would evaluate this condition before
> acquiring the s_writepages_rwsem read lock, which would trip the warning.
> Should this check be moved after acquiring the read lock?
> 

This is right, and the issue is worse than a spurious WARN, proceeding
with mismatched a_ops in that window can corrupt buffer_head-backed
folios. Sequence:

writeback         change journal flag      new write
---------         -------------------      ---------
do_writepages()
   a_ops->writepages resolved
   = ext4_iomap_writepages
   WARN check passes
   (BUFFERED_IOMAP still set)
   <preempted>
                   filemap_invalidate_lock()
                   filemap_write_and_wait()
                   truncate_pagecache(inode, 0)
                   ext4_writepages_down_write()
                   ext4_disable_buffered_iomap()
                     -> clear BUFFERED_IOMAP
                   ext4_set_aops()
                     -> a_ops = ext4_journalled_aops
                   release write lock + invalidate
                                            write() syscall
                                            ext4_journalled_aops
                                            create_empty_buffers()
                                            folio->private = buffer_head
resumed
ext4_writepages_down_read()
iomap_writepages()
   ifs_alloc(folio) sees folio->private != NULL
   -> returns it as iomap_folio_state
   -> actually a buffer_head pointer
   ifs->state, ifs->state_lock accessed
   -> memory/data corruption

So we should move the BUFFERED_IOMAP flag check under the
s_writepages_rwsem in ext4_iomap_writepages() and drop both
WARN_ON_ONCE() during writeback.

>>   	ret = ext4_emergency_state(sb);
>>   	if (unlikely(ret)) {
> 
> [ ... ]
> 
>> @@ -6037,6 +6053,81 @@ static int check_igot_inode(struct inode *inode, ext4_iget_flags flags,
>>   	return -EFSCORRUPTED;
>>   }
>>   
>> +/*
>> + * Determine whether an inode should use the iomap buffered I/O path.
>> + * EXT4_STATE_BUFFERED_IOMAP is generally set at inode initialization
> 
> [ ... ]
> 
>> +void ext4_enable_buffered_iomap(struct inode *inode)
>> +{
>> +	struct super_block *sb = inode->i_sb;
>> +
>> +	if (!S_ISREG(inode->i_mode))
>> +		return;
>> +	if (ext4_test_inode_flag(inode, EXT4_INODE_EA_INODE))
>> +		return;
>> +
>> +	/* Unsupported Features */
>> +	if (ext4_has_feature_inline_data(sb))
>> +		return;
>> +	if (ext4_has_feature_verity(sb))
>> +		return;
>> +	if (ext4_has_feature_encrypt(sb))
>> +		return;
> 
> [Severity: Critical]
> Could checking the superblock features instead of the inode flags here lead
> to a security bypass if the filesystem is manipulated offline?
> 
> If an attacker clears the encryption or verity superblock features offline,
> the kernel still sets S_ENCRYPTED or S_VERITY on individual inodes that have
> the flags set.
> 
> However, these superblock checks would fall through, allowing the encrypted
> or verity inodes to be assigned to the iomap buffered I/O path. Since the
> ext4 iomap path currently lacks fscrypt and fsverity integration, would this
> cause writes to submit plaintext bios and reads to skip cryptographic
> verification?
> 
> Would it be safer to check IS_ENCRYPTED(inode) and
> ext4_test_inode_flag(inode, EXT4_INODE_VERITY) directly?
> 

This is a false positive. I don't think we need to consider
offline filesystem corruption here.

Thanks,
Yi.

>> +	if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA ||
>> +	    ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA))
>> +		return;
>> +	if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
>> +		return;
>> +
>> +	ext4_set_inode_state(inode, EXT4_STATE_BUFFERED_IOMAP);
>
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.