Commit 959f7584 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Theodore Ts'o

ext4: fix fiemap size checks for bitmap files

Add an extra validation of the len parameter, as for ext4 some files
might have smaller file size limits than others.  This also means the
redundant size check in ext4_ioctl_get_es_cache can go away, as all
size checking is done in the shared fiemap handler.
Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarRitesh Harjani <riteshh@linux.ibm.com>
Reviewed-by: default avatarJan Kara <jack@suse.cz>
Link: https://lore.kernel.org/r/20200505154324.3226743-3-hch@lst.deSigned-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
parent 9f44eda1
...@@ -4832,6 +4832,28 @@ static const struct iomap_ops ext4_iomap_xattr_ops = { ...@@ -4832,6 +4832,28 @@ static const struct iomap_ops ext4_iomap_xattr_ops = {
.iomap_begin = ext4_iomap_xattr_begin, .iomap_begin = ext4_iomap_xattr_begin,
}; };
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;
if (*len == 0)
return -EINVAL;
if (start > maxbytes)
return -EFBIG;
/*
* Shrink request scope to what the fs can actually handle.
*/
if (*len > maxbytes || (maxbytes - *len) < start)
*len = maxbytes - start;
return 0;
}
static int _ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, static int _ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
__u64 start, __u64 len, bool from_es_cache) __u64 start, __u64 len, bool from_es_cache)
{ {
...@@ -4852,6 +4874,15 @@ static int _ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, ...@@ -4852,6 +4874,15 @@ static int _ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
if (fiemap_check_flags(fieinfo, ext4_fiemap_flags)) if (fiemap_check_flags(fieinfo, ext4_fiemap_flags))
return -EBADR; return -EBADR;
/*
* For bitmap files the maximum size limit could be smaller than
* s_maxbytes, so check len here manually instead of just relying on the
* generic check.
*/
error = ext4_fiemap_check_ranges(inode, start, &len);
if (error)
return error;
if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) { if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) {
fieinfo->fi_flags &= ~FIEMAP_FLAG_XATTR; fieinfo->fi_flags &= ~FIEMAP_FLAG_XATTR;
error = iomap_fiemap(inode, fieinfo, start, len, error = iomap_fiemap(inode, fieinfo, start, len,
......
...@@ -733,29 +733,6 @@ static void ext4_fill_fsxattr(struct inode *inode, struct fsxattr *fa) ...@@ -733,29 +733,6 @@ static void ext4_fill_fsxattr(struct inode *inode, struct fsxattr *fa)
fa->fsx_projid = from_kprojid(&init_user_ns, ei->i_projid); fa->fsx_projid = from_kprojid(&init_user_ns, ei->i_projid);
} }
/* copied from fs/ioctl.c */
static int fiemap_check_ranges(struct super_block *sb,
u64 start, u64 len, u64 *new_len)
{
u64 maxbytes = (u64) sb->s_maxbytes;
*new_len = len;
if (len == 0)
return -EINVAL;
if (start > maxbytes)
return -EFBIG;
/*
* Shrink request scope to what the fs can actually handle.
*/
if (len > maxbytes || (maxbytes - len) < start)
*new_len = maxbytes - start;
return 0;
}
/* So that the fiemap access checks can't overflow on 32 bit machines. */ /* So that the fiemap access checks can't overflow on 32 bit machines. */
#define FIEMAP_MAX_EXTENTS (UINT_MAX / sizeof(struct fiemap_extent)) #define FIEMAP_MAX_EXTENTS (UINT_MAX / sizeof(struct fiemap_extent))
...@@ -765,8 +742,6 @@ static int ext4_ioctl_get_es_cache(struct file *filp, unsigned long arg) ...@@ -765,8 +742,6 @@ static int ext4_ioctl_get_es_cache(struct file *filp, unsigned long arg)
struct fiemap __user *ufiemap = (struct fiemap __user *) arg; struct fiemap __user *ufiemap = (struct fiemap __user *) arg;
struct fiemap_extent_info fieinfo = { 0, }; struct fiemap_extent_info fieinfo = { 0, };
struct inode *inode = file_inode(filp); struct inode *inode = file_inode(filp);
struct super_block *sb = inode->i_sb;
u64 len;
int error; int error;
if (copy_from_user(&fiemap, ufiemap, sizeof(fiemap))) if (copy_from_user(&fiemap, ufiemap, sizeof(fiemap)))
...@@ -775,11 +750,6 @@ static int ext4_ioctl_get_es_cache(struct file *filp, unsigned long arg) ...@@ -775,11 +750,6 @@ static int ext4_ioctl_get_es_cache(struct file *filp, unsigned long arg)
if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS) if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS)
return -EINVAL; return -EINVAL;
error = fiemap_check_ranges(sb, fiemap.fm_start, fiemap.fm_length,
&len);
if (error)
return error;
fieinfo.fi_flags = fiemap.fm_flags; fieinfo.fi_flags = fiemap.fm_flags;
fieinfo.fi_extents_max = fiemap.fm_extent_count; fieinfo.fi_extents_max = fiemap.fm_extent_count;
fieinfo.fi_extents_start = ufiemap->fm_extents; fieinfo.fi_extents_start = ufiemap->fm_extents;
...@@ -792,7 +762,8 @@ static int ext4_ioctl_get_es_cache(struct file *filp, unsigned long arg) ...@@ -792,7 +762,8 @@ static int ext4_ioctl_get_es_cache(struct file *filp, unsigned long arg)
if (fieinfo.fi_flags & FIEMAP_FLAG_SYNC) if (fieinfo.fi_flags & FIEMAP_FLAG_SYNC)
filemap_write_and_wait(inode->i_mapping); filemap_write_and_wait(inode->i_mapping);
error = ext4_get_es_cache(inode, &fieinfo, fiemap.fm_start, len); error = ext4_get_es_cache(inode, &fieinfo, fiemap.fm_start,
fiemap.fm_length);
fiemap.fm_flags = fieinfo.fi_flags; fiemap.fm_flags = fieinfo.fi_flags;
fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped; fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped;
if (copy_to_user(ufiemap, &fiemap, sizeof(fiemap))) if (copy_to_user(ufiemap, &fiemap, sizeof(fiemap)))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment