* [PATCH] ext4: fix extent status cache clearing when truncating inline data
@ 2026-10-14 2:17 Zhang Yi
2026-10-14 2:42 ` sashiko-bot
0 siblings, 1 reply; 3+ messages in thread
From: Zhang Yi @ 2026-10-14 2:17 UTC (permalink / raw)
To: linux-ext4
Cc: linux-fsdevel, linux-kernel, tytso, adilger.kernel, libaokun,
jack, ojaswin, ritesh.list, enwlinux, yi.zhang, yi.zhang,
yizhang089, chengzhihao1, yangerkun, yukuai
From: Zhang Yi <yi.zhang@huawei.com>
ext4_inline_data_truncate() clears the whole extent status tree of the
inode when it truncates the inline data, with
ext4_es_remove_extent(inode, 0, EXT_MAX_BLOCKS);
It does that when the inode still carries inline data while the
EXT4_STATE_MAY_INLINE_DATA state bit is already cleared, which is the
case once an inline to extent conversion has been initiated, for
example a delalloc buffered write larger than the inline space.
The clear is meant to drop stale extent status entries of such an inode
to prevent stale extents and stale reserved space after the conversion,
but the range is too wide. It also drops the entries of the blocks
below the new end of file, that is the blocks that stay in the file.
When writeback of these blocks starts later, the lookup of such a block
misses the extent status tree, falls back to the extent tree and caches
a hole entry for it. Those blocks then have dirty, mapped and delayed
buffers together with a hole extent status entry. This is an
unexpected inconsistency.
This problem has been hidden so far because we allocate blocks and new
extents for them during writeback. But once [1] is merged during the
iomap conversion, the writeback behavior will be normalized, and it will
no longer allocate blocks for holes, thus exposing this problem.
Therefore, restrict the range to the blocks that are actually truncated
away, and pass EXT_MAX_BLOCKS minus lblk as the length.
[1] https://lore.kernel.org/linux-ext4/20261009103033.2920530-9-yi.zhang@huaweicloud.com/
Reported-by: syzbot ci <syzbot+ci46f64f7b6a2a188e@syzkaller.appspotmail.com>
Closes: https://lore.kernel.org/linux-ext4/6ac92a4c.70ed5d86.2ac85.0072.GAE@google.com/
Fixes: 0add491df4e5 ("ext4: remove extent cache entries when truncating inline data")
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
---
fs/ext4/inline.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index ceee69a66482b..b630e56815bb5 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -1905,8 +1905,12 @@ int ext4_inline_data_truncate(struct inode *inode, int *has_inline)
* the extent status cache must be cleared to avoid leaving
* behind stale delayed allocated extent entries
*/
- if (!ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA))
- ext4_es_remove_extent(inode, 0, EXT_MAX_BLOCKS);
+ if (!ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) {
+ ext4_lblk_t lblk = EXT4_B_TO_LBLK(inode, i_size);
+
+ ext4_es_remove_extent(inode, lblk,
+ EXT_MAX_BLOCKS - lblk);
+ }
/* Clear the content in the xattr space. */
if (inline_size > EXT4_MIN_INLINE_DATA_SIZE) {
--
2.52.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] ext4: fix extent status cache clearing when truncating inline data
2026-10-14 2:17 [PATCH] ext4: fix extent status cache clearing when truncating inline data Zhang Yi
@ 2026-10-14 2:42 ` sashiko-bot
2026-10-14 7:14 ` Zhang Yi
0 siblings, 1 reply; 3+ messages in thread
From: sashiko-bot @ 2026-10-14 2:42 UTC (permalink / raw)
To: Zhang Yi, linux-ext4; +Cc: tytso
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] ext4: integer truncation of i_size in ext4_inline_data_truncate()
--
commit dadd6832c867f600a26266f1d17a57f7cfd12f24
Author: Zhang Yi <yi.zhang@huawei.com>
Subject: ext4: fix extent status cache clearing when truncating inline data
This commit restricts the extent status cache clear range during inline data
truncation. Instead of dropping the whole extent status tree, it clears only
the blocks that are actually truncated away to prevent inconsistencies with
dirty, mapped, and delayed buffers.
Link: https://lore.kernel.org/linux-ext4/20261009103033.2920530-9-yi.zhang@huaweicloud.com/
Link: https://lore.kernel.org/linux-ext4/6ac92a4c.70ed5d86.2ac85.0072.GAE@google.com/
> diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
> index ceee69a66482b..b630e56815bb5 100644
> --- a/fs/ext4/inline.c
> +++ b/fs/ext4/inline.c
[ ... ]
> @@ -1905,8 +1905,12 @@ int ext4_inline_data_truncate(struct inode *inode, int *has_inline)
> * the extent status cache must be cleared to avoid leaving
> * behind stale delayed allocated extent entries
> */
> - if (!ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA))
> - ext4_es_remove_extent(inode, 0, EXT_MAX_BLOCKS);
> + if (!ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) {
> + ext4_lblk_t lblk = EXT4_B_TO_LBLK(inode, i_size);
> +
> + ext4_es_remove_extent(inode, lblk,
> + EXT_MAX_BLOCKS - lblk);
> + }
[Severity: High]
Does this calculation inadvertently drop valid delayed allocation extents on
32-bit architectures for files larger than 4GB?
Looking at ext4_inline_data_truncate(), the local variable i_size is
declared as a size_t:
fs/ext4/inline.c:ext4_inline_data_truncate() {
size_t i_size;
...
i_size = inode->i_size;
EXT4_I(inode)->i_disksize = i_size;
...
}
On 32-bit architectures, size_t is 32-bit, while inode->i_size is a 64-bit
loff_t.
If a file with inline data is extended beyond 4GB via a delayed allocation
write, inode->i_size successfully updates to >4GB. A subsequent failed write
past EOF (e.g., from ENOSPC) triggers ext4_truncate_failed_write(), which
calls ext4_inline_data_truncate().
Because i_size is a 32-bit size_t, the 64-bit inode->i_size wraps around
(e.g., 4GB + 10 truncates to 10). When this patch uses the truncated i_size
to calculate lblk, lblk incorrectly evaluates to a block near 0. This
results in dropping valid delayed allocation extents across the entire >4GB
file.
Additionally, the preexisting code assigns this truncated 32-bit i_size to
the 64-bit EXT4_I(inode)->i_disksize, corrupting the on-disk size. Should
i_size be changed to loff_t to prevent both the incorrect extent removal
introduced here, and the disk size corruption?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261014021714.2382353-1-yi.zhang@huaweicloud.com?part=1
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] ext4: fix extent status cache clearing when truncating inline data
2026-10-14 2:42 ` sashiko-bot
@ 2026-10-14 7:14 ` Zhang Yi
0 siblings, 0 replies; 3+ messages in thread
From: Zhang Yi @ 2026-10-14 7:14 UTC (permalink / raw)
To: sashiko-reviews, Zhang Yi, linux-ext4; +Cc: tytso
On 10/14/2026 10:42 AM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] ext4: integer truncation of i_size in ext4_inline_data_truncate()
> --
>
> commit dadd6832c867f600a26266f1d17a57f7cfd12f24
> Author: Zhang Yi <yi.zhang@huawei.com>
> Subject: ext4: fix extent status cache clearing when truncating inline data
>
> This commit restricts the extent status cache clear range during inline data
> truncation. Instead of dropping the whole extent status tree, it clears only
> the blocks that are actually truncated away to prevent inconsistencies with
> dirty, mapped, and delayed buffers.
>
> Link: https://lore.kernel.org/linux-ext4/20261009103033.2920530-9-yi.zhang@huaweicloud.com/
> Link: https://lore.kernel.org/linux-ext4/6ac92a4c.70ed5d86.2ac85.0072.GAE@google.com/
>
>> diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
>> index ceee69a66482b..b630e56815bb5 100644
>> --- a/fs/ext4/inline.c
>> +++ b/fs/ext4/inline.c
> [ ... ]
>> @@ -1905,8 +1905,12 @@ int ext4_inline_data_truncate(struct inode *inode, int *has_inline)
>> * the extent status cache must be cleared to avoid leaving
>> * behind stale delayed allocated extent entries
>> */
>> - if (!ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA))
>> - ext4_es_remove_extent(inode, 0, EXT_MAX_BLOCKS);
>> + if (!ext4_test_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA)) {
>> + ext4_lblk_t lblk = EXT4_B_TO_LBLK(inode, i_size);
>> +
>> + ext4_es_remove_extent(inode, lblk,
>> + EXT_MAX_BLOCKS - lblk);
>> + }
>
> [Severity: High]
> Does this calculation inadvertently drop valid delayed allocation extents on
> 32-bit architectures for files larger than 4GB?
>
> Looking at ext4_inline_data_truncate(), the local variable i_size is
> declared as a size_t:
>
> fs/ext4/inline.c:ext4_inline_data_truncate() {
> size_t i_size;
> ...
> i_size = inode->i_size;
> EXT4_I(inode)->i_disksize = i_size;
> ...
> }
>
> On 32-bit architectures, size_t is 32-bit, while inode->i_size is a 64-bit
> loff_t.
>
> If a file with inline data is extended beyond 4GB via a delayed allocation
> write, inode->i_size successfully updates to >4GB. A subsequent failed write
> past EOF (e.g., from ENOSPC) triggers ext4_truncate_failed_write(), which
> calls ext4_inline_data_truncate().
>
> Because i_size is a 32-bit size_t, the 64-bit inode->i_size wraps around
> (e.g., 4GB + 10 truncates to 10). When this patch uses the truncated i_size
> to calculate lblk, lblk incorrectly evaluates to a block near 0. This
> results in dropping valid delayed allocation extents across the entire >4GB
> file.
>
> Additionally, the preexisting code assigns this truncated 32-bit i_size to
> the 64-bit EXT4_I(inode)->i_disksize, corrupting the on-disk size. Should
> i_size be changed to loff_t to prevent both the incorrect extent removal
> introduced here, and the disk size corruption?
>
This is not reachable today. ext4_inline_data_truncate() is only called
from ext4_truncate() when the inode still has inline data, and ext4
keeps the size of such an inode below the inline capacity. Every path
that can grow the size past it converts the inline data to extents first,
which destroys the inline data, so the inode cannot carry inline data
any more. So a file that still has inline data when
ext4_inline_data_truncate() runs has a size of at most a few bytes.
That said, in the future it would indeed be more appropriate to change
i_size to loff_t, but this patch doesn't need it.
Thanks,
Yi.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-14 7:14 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-14 2:17 [PATCH] ext4: fix extent status cache clearing when truncating inline data Zhang Yi
2026-10-14 2:42 ` sashiko-bot
2026-10-14 7:14 ` Zhang Yi
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.