* [PATCH 2/4] ext4: fix incorrect punch max_end
2025-04-30 1:12 [PATCH 1/4] ext4: fix out of bounds punch offset Zhang Yi
@ 2025-04-30 1:12 ` Zhang Yi
2025-04-30 7:54 ` Jan Kara
2025-04-30 8:18 ` Jan Kara
2025-04-30 1:13 ` [PATCH 3/4] ext4: factor out ext4_get_maxbytes() Zhang Yi
` (2 subsequent siblings)
3 siblings, 2 replies; 14+ messages in thread
From: Zhang Yi @ 2025-04-30 1:12 UTC (permalink / raw)
To: linux-ext4
Cc: linux-fsdevel, linux-kernel, tytso, adilger.kernel, jack,
wanghaichi0403, yi.zhang, yi.zhang, libaokun1, yukuai3, yangerkun
From: Zhang Yi <yi.zhang@huawei.com>
For the extents inodes, the maxbytes should be sb->s_maxbytes instead of
sbi->s_bitmap_maxbytes. Correct the maxbytes value to correct the
behavior of punch hole.
Fixes: 2da376228a24 ("ext4: limit length to bitmap_maxbytes - blocksize in punch_hole")
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
---
fs/ext4/inode.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 4ec4a80b6879..f9725e6347c7 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -4006,7 +4006,7 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
struct inode *inode = file_inode(file);
struct super_block *sb = inode->i_sb;
ext4_lblk_t start_lblk, end_lblk;
- loff_t max_end = EXT4_SB(sb)->s_bitmap_maxbytes - sb->s_blocksize;
+ loff_t max_end;
loff_t end = offset + length;
handle_t *handle;
unsigned int credits;
@@ -4015,6 +4015,12 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
trace_ext4_punch_hole(inode, offset, length, 0);
WARN_ON_ONCE(!inode_is_locked(inode));
+ if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
+ max_end = sb->s_maxbytes;
+ else
+ max_end = EXT4_SB(sb)->s_bitmap_maxbytes;
+ max_end -= sb->s_blocksize;
+
/* No need to punch hole beyond i_size */
if (offset >= inode->i_size || offset >= max_end)
return 0;
--
2.46.1
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH 2/4] ext4: fix incorrect punch max_end
2025-04-30 1:12 ` [PATCH 2/4] ext4: fix incorrect punch max_end Zhang Yi
@ 2025-04-30 7:54 ` Jan Kara
2025-04-30 8:18 ` Jan Kara
1 sibling, 0 replies; 14+ messages in thread
From: Jan Kara @ 2025-04-30 7:54 UTC (permalink / raw)
To: Zhang Yi
Cc: linux-ext4, linux-fsdevel, linux-kernel, tytso, adilger.kernel,
jack, wanghaichi0403, yi.zhang, libaokun1, yukuai3, yangerkun
On Wed 30-04-25 09:12:59, Zhang Yi wrote:
> From: Zhang Yi <yi.zhang@huawei.com>
>
> For the extents inodes, the maxbytes should be sb->s_maxbytes instead of
> sbi->s_bitmap_maxbytes. Correct the maxbytes value to correct the
> behavior of punch hole.
>
> Fixes: 2da376228a24 ("ext4: limit length to bitmap_maxbytes - blocksize in punch_hole")
> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Looks good. Thanks! Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> fs/ext4/inode.c | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index 4ec4a80b6879..f9725e6347c7 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -4006,7 +4006,7 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
> struct inode *inode = file_inode(file);
> struct super_block *sb = inode->i_sb;
> ext4_lblk_t start_lblk, end_lblk;
> - loff_t max_end = EXT4_SB(sb)->s_bitmap_maxbytes - sb->s_blocksize;
> + loff_t max_end;
> loff_t end = offset + length;
> handle_t *handle;
> unsigned int credits;
> @@ -4015,6 +4015,12 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
> trace_ext4_punch_hole(inode, offset, length, 0);
> WARN_ON_ONCE(!inode_is_locked(inode));
>
> + if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
> + max_end = sb->s_maxbytes;
> + else
> + max_end = EXT4_SB(sb)->s_bitmap_maxbytes;
> + max_end -= sb->s_blocksize;
> +
> /* No need to punch hole beyond i_size */
> if (offset >= inode->i_size || offset >= max_end)
> return 0;
> --
> 2.46.1
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH 2/4] ext4: fix incorrect punch max_end
2025-04-30 1:12 ` [PATCH 2/4] ext4: fix incorrect punch max_end Zhang Yi
2025-04-30 7:54 ` Jan Kara
@ 2025-04-30 8:18 ` Jan Kara
2025-04-30 8:44 ` Zhang Yi
1 sibling, 1 reply; 14+ messages in thread
From: Jan Kara @ 2025-04-30 8:18 UTC (permalink / raw)
To: Zhang Yi
Cc: linux-ext4, linux-fsdevel, linux-kernel, tytso, adilger.kernel,
jack, wanghaichi0403, yi.zhang, libaokun1, yukuai3, yangerkun
On Wed 30-04-25 09:12:59, Zhang Yi wrote:
> From: Zhang Yi <yi.zhang@huawei.com>
>
> For the extents inodes, the maxbytes should be sb->s_maxbytes instead of
> sbi->s_bitmap_maxbytes. Correct the maxbytes value to correct the
> behavior of punch hole.
>
> Fixes: 2da376228a24 ("ext4: limit length to bitmap_maxbytes - blocksize in punch_hole")
> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Thinking about this some more...
> @@ -4015,6 +4015,12 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
> trace_ext4_punch_hole(inode, offset, length, 0);
> WARN_ON_ONCE(!inode_is_locked(inode));
>
> + if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
> + max_end = sb->s_maxbytes;
> + else
> + max_end = EXT4_SB(sb)->s_bitmap_maxbytes;
> + max_end -= sb->s_blocksize;
I think the -= sb->s_blocksize is needed only for indirect-block based
scheme (due to an implementation quirk in ext4_ind_remove_space()). But
ext4_ext_remove_space() should be fine with punch hole ending right at
sb->s_maxbytes. And since I find it somewhat odd that you can create file
upto s_maxbytes but cannot punch hole to the end, it'd limit that behavior
as much as possible. Ideally we'd fix ext4_ind_remove_space() but I can't
be really bothered for the ancient format...
Honza
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH 2/4] ext4: fix incorrect punch max_end
2025-04-30 8:18 ` Jan Kara
@ 2025-04-30 8:44 ` Zhang Yi
2025-04-30 10:09 ` Jan Kara
0 siblings, 1 reply; 14+ messages in thread
From: Zhang Yi @ 2025-04-30 8:44 UTC (permalink / raw)
To: Jan Kara
Cc: linux-ext4, linux-fsdevel, linux-kernel, tytso, adilger.kernel,
wanghaichi0403, yi.zhang, libaokun1, yukuai3, yangerkun
On 2025/4/30 16:18, Jan Kara wrote:
> On Wed 30-04-25 09:12:59, Zhang Yi wrote:
>> From: Zhang Yi <yi.zhang@huawei.com>
>>
>> For the extents inodes, the maxbytes should be sb->s_maxbytes instead of
>> sbi->s_bitmap_maxbytes. Correct the maxbytes value to correct the
>> behavior of punch hole.
>>
>> Fixes: 2da376228a24 ("ext4: limit length to bitmap_maxbytes - blocksize in punch_hole")
>> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
>
> Thinking about this some more...
>
>> @@ -4015,6 +4015,12 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
>> trace_ext4_punch_hole(inode, offset, length, 0);
>> WARN_ON_ONCE(!inode_is_locked(inode));
>>
>> + if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
>> + max_end = sb->s_maxbytes;
>> + else
>> + max_end = EXT4_SB(sb)->s_bitmap_maxbytes;
>> + max_end -= sb->s_blocksize;
>
> I think the -= sb->s_blocksize is needed only for indirect-block based
> scheme (due to an implementation quirk in ext4_ind_remove_space()). But
> ext4_ext_remove_space() should be fine with punch hole ending right at
> sb->s_maxbytes. And since I find it somewhat odd that you can create file
> upto s_maxbytes but cannot punch hole to the end, it'd limit that behavior
> as much as possible. Ideally we'd fix ext4_ind_remove_space() but I can't
> be really bothered for the ancient format...
>
Yes, I share your feelings. Currently, we do not seem to have any
practical issues. To maintain consistent behavior between the two inode
types and to keep the code simple, I retained the -= sb->s_blocksize
operation. Would you suggest that we should at least address the extents
inodes by removing the -=sb->s_blocksize now?
Thanks,
Yi.
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH 2/4] ext4: fix incorrect punch max_end
2025-04-30 8:44 ` Zhang Yi
@ 2025-04-30 10:09 ` Jan Kara
2025-04-30 10:54 ` Zhang Yi
0 siblings, 1 reply; 14+ messages in thread
From: Jan Kara @ 2025-04-30 10:09 UTC (permalink / raw)
To: Zhang Yi
Cc: Jan Kara, linux-ext4, linux-fsdevel, linux-kernel, tytso,
adilger.kernel, wanghaichi0403, yi.zhang, libaokun1, yukuai3,
yangerkun
On Wed 30-04-25 16:44:25, Zhang Yi wrote:
> On 2025/4/30 16:18, Jan Kara wrote:
> > On Wed 30-04-25 09:12:59, Zhang Yi wrote:
> >> From: Zhang Yi <yi.zhang@huawei.com>
> >>
> >> For the extents inodes, the maxbytes should be sb->s_maxbytes instead of
> >> sbi->s_bitmap_maxbytes. Correct the maxbytes value to correct the
> >> behavior of punch hole.
> >>
> >> Fixes: 2da376228a24 ("ext4: limit length to bitmap_maxbytes - blocksize in punch_hole")
> >> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
> >
> > Thinking about this some more...
> >
> >> @@ -4015,6 +4015,12 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
> >> trace_ext4_punch_hole(inode, offset, length, 0);
> >> WARN_ON_ONCE(!inode_is_locked(inode));
> >>
> >> + if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
> >> + max_end = sb->s_maxbytes;
> >> + else
> >> + max_end = EXT4_SB(sb)->s_bitmap_maxbytes;
> >> + max_end -= sb->s_blocksize;
> >
> > I think the -= sb->s_blocksize is needed only for indirect-block based
> > scheme (due to an implementation quirk in ext4_ind_remove_space()). But
> > ext4_ext_remove_space() should be fine with punch hole ending right at
> > sb->s_maxbytes. And since I find it somewhat odd that you can create file
> > upto s_maxbytes but cannot punch hole to the end, it'd limit that behavior
> > as much as possible. Ideally we'd fix ext4_ind_remove_space() but I can't
> > be really bothered for the ancient format...
> >
>
> Yes, I share your feelings. Currently, we do not seem to have any
> practical issues. To maintain consistent behavior between the two inode
> types and to keep the code simple, I retained the -= sb->s_blocksize
> operation. Would you suggest that we should at least address the extents
> inodes by removing the -=sb->s_blocksize now?
Yes, what I'm suggesting is that we keep -=sb->s_blocksize specific for the
case !ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS).
Honza
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 14+ messages in thread* Re: [PATCH 2/4] ext4: fix incorrect punch max_end
2025-04-30 10:09 ` Jan Kara
@ 2025-04-30 10:54 ` Zhang Yi
0 siblings, 0 replies; 14+ messages in thread
From: Zhang Yi @ 2025-04-30 10:54 UTC (permalink / raw)
To: Jan Kara
Cc: linux-ext4, linux-fsdevel, linux-kernel, tytso, adilger.kernel,
wanghaichi0403, yi.zhang, libaokun1, yukuai3, yangerkun
On 2025/4/30 18:09, Jan Kara wrote:
> On Wed 30-04-25 16:44:25, Zhang Yi wrote:
>> On 2025/4/30 16:18, Jan Kara wrote:
>>> On Wed 30-04-25 09:12:59, Zhang Yi wrote:
>>>> From: Zhang Yi <yi.zhang@huawei.com>
>>>>
>>>> For the extents inodes, the maxbytes should be sb->s_maxbytes instead of
>>>> sbi->s_bitmap_maxbytes. Correct the maxbytes value to correct the
>>>> behavior of punch hole.
>>>>
>>>> Fixes: 2da376228a24 ("ext4: limit length to bitmap_maxbytes - blocksize in punch_hole")
>>>> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
>>>
>>> Thinking about this some more...
>>>
>>>> @@ -4015,6 +4015,12 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
>>>> trace_ext4_punch_hole(inode, offset, length, 0);
>>>> WARN_ON_ONCE(!inode_is_locked(inode));
>>>>
>>>> + if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
>>>> + max_end = sb->s_maxbytes;
>>>> + else
>>>> + max_end = EXT4_SB(sb)->s_bitmap_maxbytes;
>>>> + max_end -= sb->s_blocksize;
>>>
>>> I think the -= sb->s_blocksize is needed only for indirect-block based
>>> scheme (due to an implementation quirk in ext4_ind_remove_space()). But
>>> ext4_ext_remove_space() should be fine with punch hole ending right at
>>> sb->s_maxbytes. And since I find it somewhat odd that you can create file
>>> upto s_maxbytes but cannot punch hole to the end, it'd limit that behavior
>>> as much as possible. Ideally we'd fix ext4_ind_remove_space() but I can't
>>> be really bothered for the ancient format...
>>>
>>
>> Yes, I share your feelings. Currently, we do not seem to have any
>> practical issues. To maintain consistent behavior between the two inode
>> types and to keep the code simple, I retained the -= sb->s_blocksize
>> operation. Would you suggest that we should at least address the extents
>> inodes by removing the -=sb->s_blocksize now?
>
> Yes, what I'm suggesting is that we keep -=sb->s_blocksize specific for the
> case !ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS).
>
Sure. Let's do it.
Thanks,
Yi.
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 3/4] ext4: factor out ext4_get_maxbytes()
2025-04-30 1:12 [PATCH 1/4] ext4: fix out of bounds punch offset Zhang Yi
2025-04-30 1:12 ` [PATCH 2/4] ext4: fix incorrect punch max_end Zhang Yi
@ 2025-04-30 1:13 ` Zhang Yi
2025-04-30 7:55 ` Jan Kara
2025-04-30 1:13 ` [PATCH 4/4] ext4: ensure i_size is smaller than maxbytes Zhang Yi
2025-04-30 7:51 ` [PATCH 1/4] ext4: fix out of bounds punch offset Jan Kara
3 siblings, 1 reply; 14+ messages in thread
From: Zhang Yi @ 2025-04-30 1:13 UTC (permalink / raw)
To: linux-ext4
Cc: linux-fsdevel, linux-kernel, tytso, adilger.kernel, jack,
wanghaichi0403, yi.zhang, yi.zhang, libaokun1, yukuai3, yangerkun
From: Zhang Yi <yi.zhang@huawei.com>
There are several locations that get the correct maxbytes value based on
the inode's block type. It would be beneficial to extract a common
helper function to make the code more clear.
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
---
fs/ext4/ext4.h | 7 +++++++
fs/ext4/extents.c | 7 +------
fs/ext4/file.c | 7 +------
fs/ext4/inode.c | 8 +-------
4 files changed, 10 insertions(+), 19 deletions(-)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 5a20e9cd7184..8664bb5367c5 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -3378,6 +3378,13 @@ static inline unsigned int ext4_flex_bg_size(struct ext4_sb_info *sbi)
return 1 << sbi->s_log_groups_per_flex;
}
+static inline loff_t ext4_get_maxbytes(struct inode *inode)
+{
+ if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
+ return inode->i_sb->s_maxbytes;
+ return EXT4_SB(inode->i_sb)->s_bitmap_maxbytes;
+}
+
#define ext4_std_error(sb, errno) \
do { \
if ((errno)) \
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index c616a16a9f36..b294d2f35a26 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -4931,12 +4931,7 @@ static const struct iomap_ops ext4_iomap_xattr_ops = {
static int ext4_fiemap_check_ranges(struct inode *inode, u64 start, u64 *len)
{
- u64 maxbytes;
-
- if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
- maxbytes = inode->i_sb->s_maxbytes;
- else
- maxbytes = EXT4_SB(inode->i_sb)->s_bitmap_maxbytes;
+ u64 maxbytes = ext4_get_maxbytes(inode);
if (*len == 0)
return -EINVAL;
diff --git a/fs/ext4/file.c b/fs/ext4/file.c
index beb078ee4811..b845a25f7932 100644
--- a/fs/ext4/file.c
+++ b/fs/ext4/file.c
@@ -929,12 +929,7 @@ static int ext4_file_open(struct inode *inode, struct file *filp)
loff_t ext4_llseek(struct file *file, loff_t offset, int whence)
{
struct inode *inode = file->f_mapping->host;
- loff_t maxbytes;
-
- if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
- maxbytes = EXT4_SB(inode->i_sb)->s_bitmap_maxbytes;
- else
- maxbytes = inode->i_sb->s_maxbytes;
+ loff_t maxbytes = ext4_get_maxbytes(inode);
switch (whence) {
default:
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index f9725e6347c7..9f32af1241ff 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -4006,7 +4006,7 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
struct inode *inode = file_inode(file);
struct super_block *sb = inode->i_sb;
ext4_lblk_t start_lblk, end_lblk;
- loff_t max_end;
+ loff_t max_end = ext4_get_maxbytes(inode) - sb->s_blocksize;
loff_t end = offset + length;
handle_t *handle;
unsigned int credits;
@@ -4015,12 +4015,6 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
trace_ext4_punch_hole(inode, offset, length, 0);
WARN_ON_ONCE(!inode_is_locked(inode));
- if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
- max_end = sb->s_maxbytes;
- else
- max_end = EXT4_SB(sb)->s_bitmap_maxbytes;
- max_end -= sb->s_blocksize;
-
/* No need to punch hole beyond i_size */
if (offset >= inode->i_size || offset >= max_end)
return 0;
--
2.46.1
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH 3/4] ext4: factor out ext4_get_maxbytes()
2025-04-30 1:13 ` [PATCH 3/4] ext4: factor out ext4_get_maxbytes() Zhang Yi
@ 2025-04-30 7:55 ` Jan Kara
0 siblings, 0 replies; 14+ messages in thread
From: Jan Kara @ 2025-04-30 7:55 UTC (permalink / raw)
To: Zhang Yi
Cc: linux-ext4, linux-fsdevel, linux-kernel, tytso, adilger.kernel,
jack, wanghaichi0403, yi.zhang, libaokun1, yukuai3, yangerkun
On Wed 30-04-25 09:13:00, Zhang Yi wrote:
> From: Zhang Yi <yi.zhang@huawei.com>
>
> There are several locations that get the correct maxbytes value based on
> the inode's block type. It would be beneficial to extract a common
> helper function to make the code more clear.
>
> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Nice. Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> fs/ext4/ext4.h | 7 +++++++
> fs/ext4/extents.c | 7 +------
> fs/ext4/file.c | 7 +------
> fs/ext4/inode.c | 8 +-------
> 4 files changed, 10 insertions(+), 19 deletions(-)
>
> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
> index 5a20e9cd7184..8664bb5367c5 100644
> --- a/fs/ext4/ext4.h
> +++ b/fs/ext4/ext4.h
> @@ -3378,6 +3378,13 @@ static inline unsigned int ext4_flex_bg_size(struct ext4_sb_info *sbi)
> return 1 << sbi->s_log_groups_per_flex;
> }
>
> +static inline loff_t ext4_get_maxbytes(struct inode *inode)
> +{
> + if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
> + return inode->i_sb->s_maxbytes;
> + return EXT4_SB(inode->i_sb)->s_bitmap_maxbytes;
> +}
> +
> #define ext4_std_error(sb, errno) \
> do { \
> if ((errno)) \
> diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
> index c616a16a9f36..b294d2f35a26 100644
> --- a/fs/ext4/extents.c
> +++ b/fs/ext4/extents.c
> @@ -4931,12 +4931,7 @@ static const struct iomap_ops ext4_iomap_xattr_ops = {
>
> static int ext4_fiemap_check_ranges(struct inode *inode, u64 start, u64 *len)
> {
> - u64 maxbytes;
> -
> - if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
> - maxbytes = inode->i_sb->s_maxbytes;
> - else
> - maxbytes = EXT4_SB(inode->i_sb)->s_bitmap_maxbytes;
> + u64 maxbytes = ext4_get_maxbytes(inode);
>
> if (*len == 0)
> return -EINVAL;
> diff --git a/fs/ext4/file.c b/fs/ext4/file.c
> index beb078ee4811..b845a25f7932 100644
> --- a/fs/ext4/file.c
> +++ b/fs/ext4/file.c
> @@ -929,12 +929,7 @@ static int ext4_file_open(struct inode *inode, struct file *filp)
> loff_t ext4_llseek(struct file *file, loff_t offset, int whence)
> {
> struct inode *inode = file->f_mapping->host;
> - loff_t maxbytes;
> -
> - if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)))
> - maxbytes = EXT4_SB(inode->i_sb)->s_bitmap_maxbytes;
> - else
> - maxbytes = inode->i_sb->s_maxbytes;
> + loff_t maxbytes = ext4_get_maxbytes(inode);
>
> switch (whence) {
> default:
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index f9725e6347c7..9f32af1241ff 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -4006,7 +4006,7 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
> struct inode *inode = file_inode(file);
> struct super_block *sb = inode->i_sb;
> ext4_lblk_t start_lblk, end_lblk;
> - loff_t max_end;
> + loff_t max_end = ext4_get_maxbytes(inode) - sb->s_blocksize;
> loff_t end = offset + length;
> handle_t *handle;
> unsigned int credits;
> @@ -4015,12 +4015,6 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
> trace_ext4_punch_hole(inode, offset, length, 0);
> WARN_ON_ONCE(!inode_is_locked(inode));
>
> - if (ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))
> - max_end = sb->s_maxbytes;
> - else
> - max_end = EXT4_SB(sb)->s_bitmap_maxbytes;
> - max_end -= sb->s_blocksize;
> -
> /* No need to punch hole beyond i_size */
> if (offset >= inode->i_size || offset >= max_end)
> return 0;
> --
> 2.46.1
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 4/4] ext4: ensure i_size is smaller than maxbytes
2025-04-30 1:12 [PATCH 1/4] ext4: fix out of bounds punch offset Zhang Yi
2025-04-30 1:12 ` [PATCH 2/4] ext4: fix incorrect punch max_end Zhang Yi
2025-04-30 1:13 ` [PATCH 3/4] ext4: factor out ext4_get_maxbytes() Zhang Yi
@ 2025-04-30 1:13 ` Zhang Yi
2025-04-30 3:20 ` Matthew Wilcox
2025-04-30 8:01 ` Jan Kara
2025-04-30 7:51 ` [PATCH 1/4] ext4: fix out of bounds punch offset Jan Kara
3 siblings, 2 replies; 14+ messages in thread
From: Zhang Yi @ 2025-04-30 1:13 UTC (permalink / raw)
To: linux-ext4
Cc: linux-fsdevel, linux-kernel, tytso, adilger.kernel, jack,
wanghaichi0403, yi.zhang, yi.zhang, libaokun1, yukuai3, yangerkun
From: Zhang Yi <yi.zhang@huawei.com>
The inode i_size cannot be larger than maxbytes, check it while loading
inode from the disk.
Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
---
fs/ext4/inode.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 9f32af1241ff..2bd2a4f4d9d7 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -4916,7 +4916,8 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
ei->i_file_acl |=
((__u64)le16_to_cpu(raw_inode->i_file_acl_high)) << 32;
inode->i_size = ext4_isize(sb, raw_inode);
- if ((size = i_size_read(inode)) < 0) {
+ size = i_size_read(inode);
+ if (size < 0 || size > ext4_get_maxbytes(inode)) {
ext4_error_inode(inode, function, line, 0,
"iget: bad i_size value: %lld", size);
ret = -EFSCORRUPTED;
--
2.46.1
^ permalink raw reply related [flat|nested] 14+ messages in thread* Re: [PATCH 4/4] ext4: ensure i_size is smaller than maxbytes
2025-04-30 1:13 ` [PATCH 4/4] ext4: ensure i_size is smaller than maxbytes Zhang Yi
@ 2025-04-30 3:20 ` Matthew Wilcox
2025-04-30 3:32 ` Zhang Yi
2025-04-30 8:01 ` Jan Kara
1 sibling, 1 reply; 14+ messages in thread
From: Matthew Wilcox @ 2025-04-30 3:20 UTC (permalink / raw)
To: Zhang Yi
Cc: linux-ext4, linux-fsdevel, linux-kernel, tytso, adilger.kernel,
jack, wanghaichi0403, yi.zhang, libaokun1, yukuai3, yangerkun
On Wed, Apr 30, 2025 at 09:13:01AM +0800, Zhang Yi wrote:
> From: Zhang Yi <yi.zhang@huawei.com>
>
> The inode i_size cannot be larger than maxbytes, check it while loading
> inode from the disk.
With this patch, why do we need patch 1?
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 4/4] ext4: ensure i_size is smaller than maxbytes
2025-04-30 3:20 ` Matthew Wilcox
@ 2025-04-30 3:32 ` Zhang Yi
0 siblings, 0 replies; 14+ messages in thread
From: Zhang Yi @ 2025-04-30 3:32 UTC (permalink / raw)
To: Matthew Wilcox
Cc: linux-ext4, linux-fsdevel, linux-kernel, tytso, adilger.kernel,
jack, wanghaichi0403, yi.zhang, libaokun1, yukuai3, yangerkun
On 2025/4/30 11:20, Matthew Wilcox wrote:
> On Wed, Apr 30, 2025 at 09:13:01AM +0800, Zhang Yi wrote:
>> From: Zhang Yi <yi.zhang@huawei.com>
>>
>> The inode i_size cannot be larger than maxbytes, check it while loading
>> inode from the disk.
>
> With this patch, why do we need patch 1?
The max_end in ext4_punch_hole() is maxbytes - blocksize. Therefore,
without the patch 1, it is still possible to encounter a negative
length if the offset exceeds max_end but is less than an i_size that
also exceeds max_end.
max_end i_size max_bytes
| | |
0------------------------------------
|
offset
Thanks,
Yi.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 4/4] ext4: ensure i_size is smaller than maxbytes
2025-04-30 1:13 ` [PATCH 4/4] ext4: ensure i_size is smaller than maxbytes Zhang Yi
2025-04-30 3:20 ` Matthew Wilcox
@ 2025-04-30 8:01 ` Jan Kara
1 sibling, 0 replies; 14+ messages in thread
From: Jan Kara @ 2025-04-30 8:01 UTC (permalink / raw)
To: Zhang Yi
Cc: linux-ext4, linux-fsdevel, linux-kernel, tytso, adilger.kernel,
jack, wanghaichi0403, yi.zhang, libaokun1, yukuai3, yangerkun
On Wed 30-04-25 09:13:01, Zhang Yi wrote:
> From: Zhang Yi <yi.zhang@huawei.com>
>
> The inode i_size cannot be larger than maxbytes, check it while loading
> inode from the disk.
>
> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Looks good. Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> fs/ext4/inode.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index 9f32af1241ff..2bd2a4f4d9d7 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -4916,7 +4916,8 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
> ei->i_file_acl |=
> ((__u64)le16_to_cpu(raw_inode->i_file_acl_high)) << 32;
> inode->i_size = ext4_isize(sb, raw_inode);
> - if ((size = i_size_read(inode)) < 0) {
> + size = i_size_read(inode);
> + if (size < 0 || size > ext4_get_maxbytes(inode)) {
> ext4_error_inode(inode, function, line, 0,
> "iget: bad i_size value: %lld", size);
> ret = -EFSCORRUPTED;
> --
> 2.46.1
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/4] ext4: fix out of bounds punch offset
2025-04-30 1:12 [PATCH 1/4] ext4: fix out of bounds punch offset Zhang Yi
` (2 preceding siblings ...)
2025-04-30 1:13 ` [PATCH 4/4] ext4: ensure i_size is smaller than maxbytes Zhang Yi
@ 2025-04-30 7:51 ` Jan Kara
3 siblings, 0 replies; 14+ messages in thread
From: Jan Kara @ 2025-04-30 7:51 UTC (permalink / raw)
To: Zhang Yi
Cc: linux-ext4, linux-fsdevel, linux-kernel, tytso, adilger.kernel,
jack, wanghaichi0403, yi.zhang, libaokun1, yukuai3, yangerkun
On Wed 30-04-25 09:12:58, Zhang Yi wrote:
> From: Zhang Yi <yi.zhang@huawei.com>
>
> Punching a hole with a start offset that exceeds max_end is not
> permitted and will result in a negative length in the
> truncate_inode_partial_folio() function while truncating the page cache,
> potentially leading to undesirable consequences.
>
> A simple reproducer:
>
> truncate -s 9895604649994 /mnt/foo
> xfs_io -c "pwrite 8796093022208 4096" /mnt/foo
> xfs_io -c "fpunch 8796093022213 25769803777" /mnt/foo
>
> kernel BUG at include/linux/highmem.h:275!
> Oops: invalid opcode: 0000 [#1] SMP PTI
> CPU: 3 UID: 0 PID: 710 Comm: xfs_io Not tainted 6.15.0-rc3
> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.3-2.fc40 04/01/2014
> RIP: 0010:zero_user_segments.constprop.0+0xd7/0x110
> RSP: 0018:ffffc90001cf3b38 EFLAGS: 00010287
> RAX: 0000000000000005 RBX: ffffea0001485e40 RCX: 0000000000001000
> RDX: 000000000040b000 RSI: 0000000000000005 RDI: 000000000040b000
> RBP: 000000000040affb R08: ffff888000000000 R09: ffffea0000000000
> R10: 0000000000000003 R11: 00000000fffc7fc5 R12: 0000000000000005
> R13: 000000000040affb R14: ffffea0001485e40 R15: ffff888031cd3000
> FS: 00007f4f63d0b780(0000) GS:ffff8880d337d000(0000)
> knlGS:0000000000000000
> CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 000000001ae0b038 CR3: 00000000536aa000 CR4: 00000000000006f0
> DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
> DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
> Call Trace:
> <TASK>
> truncate_inode_partial_folio+0x3dd/0x620
> truncate_inode_pages_range+0x226/0x720
> ? bdev_getblk+0x52/0x3e0
> ? ext4_get_group_desc+0x78/0x150
> ? crc32c_arch+0xfd/0x180
> ? __ext4_get_inode_loc+0x18c/0x840
> ? ext4_inode_csum+0x117/0x160
> ? jbd2_journal_dirty_metadata+0x61/0x390
> ? __ext4_handle_dirty_metadata+0xa0/0x2b0
> ? kmem_cache_free+0x90/0x5a0
> ? jbd2_journal_stop+0x1d5/0x550
> ? __ext4_journal_stop+0x49/0x100
> truncate_pagecache_range+0x50/0x80
> ext4_truncate_page_cache_block_range+0x57/0x3a0
> ext4_punch_hole+0x1fe/0x670
> ext4_fallocate+0x792/0x17d0
> ? __count_memcg_events+0x175/0x2a0
> vfs_fallocate+0x121/0x560
> ksys_fallocate+0x51/0xc0
> __x64_sys_fallocate+0x24/0x40
> x64_sys_call+0x18d2/0x4170
> do_syscall_64+0xa7/0x220
> entry_SYSCALL_64_after_hwframe+0x76/0x7e
>
> Fix this by filtering out cases where the punching start offset exceeds
> max_end.
>
> Fixes: 982bf37da09d ("ext4: refactor ext4_punch_hole()")
> Reported-by: Liebes Wang <wanghaichi0403@gmail.com>
> Closes: https://lore.kernel.org/linux-ext4/ac3a58f6-e686-488b-a9ee-fc041024e43d@huawei.com/
> Tested-by: Liebes Wang <wanghaichi0403@gmail.com>
> Signed-off-by: Zhang Yi <yi.zhang@huawei.com>
Looks good. Feel free to add:
Reviewed-by: Jan Kara <jack@suse.cz>
Honza
> ---
> fs/ext4/inode.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
> index 94c7d2d828a6..4ec4a80b6879 100644
> --- a/fs/ext4/inode.c
> +++ b/fs/ext4/inode.c
> @@ -4016,7 +4016,7 @@ int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
> WARN_ON_ONCE(!inode_is_locked(inode));
>
> /* No need to punch hole beyond i_size */
> - if (offset >= inode->i_size)
> + if (offset >= inode->i_size || offset >= max_end)
> return 0;
>
> /*
> --
> 2.46.1
>
--
Jan Kara <jack@suse.com>
SUSE Labs, CR
^ permalink raw reply [flat|nested] 14+ messages in thread