Skip to content

Commit ce56d21

Browse files
Brian Fostertytso
authored andcommitted
ext4: fix racy may inline data check in dio write
syzbot reports that the following warning from ext4_iomap_begin() triggers as of the commit referenced below: if (WARN_ON_ONCE(ext4_has_inline_data(inode))) return -ERANGE; This occurs during a dio write, which is never expected to encounter an inode with inline data. To enforce this behavior, ext4_dio_write_iter() checks the current inline state of the inode and clears the MAY_INLINE_DATA state flag to either fall back to buffered writes, or enforce that any other writers in progress on the inode are not allowed to create inline data. The problem is that the check for existing inline data and the state flag can span a lock cycle. For example, if the ilock is originally locked shared and subsequently upgraded to exclusive, another writer may have reacquired the lock and created inline data before the dio write task acquires the lock and proceeds. The commit referenced below loosens the lock requirements to allow some forms of unaligned dio writes to occur under shared lock, but AFAICT the inline data check was technically already racy for any dio write that would have involved a lock cycle. Regardless, lift clearing of the state bit to the same lock critical section that checks for preexisting inline data on the inode to close the race. Cc: [email protected] Reported-by: [email protected] Fixes: 310ee09 ("ext4: allow concurrent unaligned dio overwrites") Signed-off-by: Brian Foster <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Theodore Ts'o <[email protected]>
1 parent 28b95ee commit ce56d21

File tree

1 file changed

+9
-7
lines changed

1 file changed

+9
-7
lines changed

fs/ext4/file.c

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -569,18 +569,20 @@ static ssize_t ext4_dio_write_iter(struct kiocb *iocb, struct iov_iter *from)
569569
return ext4_buffered_write_iter(iocb, from);
570570
}
571571

572+
/*
573+
* Prevent inline data from being created since we are going to allocate
574+
* blocks for DIO. We know the inode does not currently have inline data
575+
* because ext4_should_use_dio() checked for it, but we have to clear
576+
* the state flag before the write checks because a lock cycle could
577+
* introduce races with other writers.
578+
*/
579+
ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
580+
572581
ret = ext4_dio_write_checks(iocb, from, &ilock_shared, &extend,
573582
&unwritten, &dio_flags);
574583
if (ret <= 0)
575584
return ret;
576585

577-
/*
578-
* Make sure inline data cannot be created anymore since we are going
579-
* to allocate blocks for DIO. We know the inode does not have any
580-
* inline data now because ext4_dio_supported() checked for that.
581-
*/
582-
ext4_clear_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
583-
584586
offset = iocb->ki_pos;
585587
count = ret;
586588

0 commit comments

Comments
 (0)