Skip to content

Commit 23fffa9

Browse files
Lukas Czernertytso
authored andcommitted
fs: move falloc collapse range check into the filesystem methods
Currently in do_fallocate in collapse range case we're checking whether offset + len is not bigger than i_size. However there is nothing which would prevent i_size from changing so the check is pointless. It should be done in the file system itself and the file system needs to make sure that i_size is not going to change. The i_size check for the other fallocate modes are also done in the filesystems. As it is now we can easily crash the kernel by having two processes doing truncate and fallocate collapse range at the same time. This can be reproduced on ext4 and it is theoretically possible on xfs even though I was not able to trigger it with this simple test. This commit removes the check from do_fallocate and adds it to the file system. Signed-off-by: Lukas Czerner <[email protected]> Signed-off-by: "Theodore Ts'o" <[email protected]> Acked-by: Dave Chinner <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]>
1 parent 8fc61d9 commit 23fffa9

File tree

3 files changed

+18
-11
lines changed

3 files changed

+18
-11
lines changed

fs/ext4/extents.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5368,8 +5368,6 @@ int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len)
53685368
loff_t new_size;
53695369
int ret;
53705370

5371-
BUG_ON(offset + len > i_size_read(inode));
5372-
53735371
/* Collapse range works only on fs block size aligned offsets. */
53745372
if (offset & (EXT4_BLOCK_SIZE(sb) - 1) ||
53755373
len & (EXT4_BLOCK_SIZE(sb) - 1))
@@ -5398,6 +5396,15 @@ int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len)
53985396
/* Take mutex lock */
53995397
mutex_lock(&inode->i_mutex);
54005398

5399+
/*
5400+
* There is no need to overlap collapse range with EOF, in which case
5401+
* it is effectively a truncate operation
5402+
*/
5403+
if (offset + len >= i_size_read(inode)) {
5404+
ret = -EINVAL;
5405+
goto out_mutex;
5406+
}
5407+
54015408
if (IS_SWAPFILE(inode)) {
54025409
ret = -ETXTBSY;
54035410
goto out_mutex;

fs/open.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -284,14 +284,6 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
284284
if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0))
285285
return -EFBIG;
286286

287-
/*
288-
* There is no need to overlap collapse range with EOF, in which case
289-
* it is effectively a truncate operation
290-
*/
291-
if ((mode & FALLOC_FL_COLLAPSE_RANGE) &&
292-
(offset + len >= i_size_read(inode)))
293-
return -EINVAL;
294-
295287
if (!file->f_op->fallocate)
296288
return -EOPNOTSUPP;
297289

fs/xfs/xfs_file.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -840,7 +840,15 @@ xfs_file_fallocate(
840840
goto out_unlock;
841841
}
842842

843-
ASSERT(offset + len < i_size_read(inode));
843+
/*
844+
* There is no need to overlap collapse range with EOF,
845+
* in which case it is effectively a truncate operation
846+
*/
847+
if (offset + len >= i_size_read(inode)) {
848+
error = -EINVAL;
849+
goto out_unlock;
850+
}
851+
844852
new_size = i_size_read(inode) - len;
845853

846854
error = xfs_collapse_file_space(ip, offset, len);

0 commit comments

Comments
 (0)