aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarrick J. Wong <darrick.wong@oracle.com>2018-10-29 19:40:46 -0400
committerDave Chinner <david@fromorbit.com>2018-10-29 19:40:46 -0400
commit9fd91a90cb9837372af24a804853e15c11aed93e (patch)
tree28bf526bcccd9d6250a7451e5c2d734a419c70f4
parent2c5773f102c9bb07d5328467f61f0a88f2f2892d (diff)
vfs: strengthen checking of file range inputs to generic_remap_checks
File range remapping, if allowed to run past the destination file's EOF, is an optimization on a regular file write. Regular file writes that extend the file length are subject to various constraints which are not checked by range cloning. This is a correctness problem because we're never allowed to touch ranges that the page cache can't support (s_maxbytes); we're not supposed to deal with large offsets (MAX_NON_LFS) if O_LARGEFILE isn't set; and we must obey resource limits (RLIMIT_FSIZE). Therefore, add these checks to the new generic_remap_checks function so that we curtail unexpected behavior. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Amir Goldstein <amir73il@gmail.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Dave Chinner <david@fromorbit.com>
-rw-r--r--mm/filemap.c84
1 files changed, 52 insertions, 32 deletions
diff --git a/mm/filemap.c b/mm/filemap.c
index 47e6bfd45a91..84b7301e41a0 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -2916,6 +2916,42 @@ struct page *read_cache_page_gfp(struct address_space *mapping,
2916EXPORT_SYMBOL(read_cache_page_gfp); 2916EXPORT_SYMBOL(read_cache_page_gfp);
2917 2917
2918/* 2918/*
2919 * Don't operate on ranges the page cache doesn't support, and don't exceed the
2920 * LFS limits. If pos is under the limit it becomes a short access. If it
2921 * exceeds the limit we return -EFBIG.
2922 */
2923static int generic_access_check_limits(struct file *file, loff_t pos,
2924 loff_t *count)
2925{
2926 struct inode *inode = file->f_mapping->host;
2927 loff_t max_size = inode->i_sb->s_maxbytes;
2928
2929 if (!(file->f_flags & O_LARGEFILE))
2930 max_size = MAX_NON_LFS;
2931
2932 if (unlikely(pos >= max_size))
2933 return -EFBIG;
2934 *count = min(*count, max_size - pos);
2935 return 0;
2936}
2937
2938static int generic_write_check_limits(struct file *file, loff_t pos,
2939 loff_t *count)
2940{
2941 loff_t limit = rlimit(RLIMIT_FSIZE);
2942
2943 if (limit != RLIM_INFINITY) {
2944 if (pos >= limit) {
2945 send_sig(SIGXFSZ, current, 0);
2946 return -EFBIG;
2947 }
2948 *count = min(*count, limit - pos);
2949 }
2950
2951 return generic_access_check_limits(file, pos, count);
2952}
2953
2954/*
2919 * Performs necessary checks before doing a write 2955 * Performs necessary checks before doing a write
2920 * 2956 *
2921 * Can adjust writing position or amount of bytes to write. 2957 * Can adjust writing position or amount of bytes to write.
@@ -2926,8 +2962,8 @@ inline ssize_t generic_write_checks(struct kiocb *iocb, struct iov_iter *from)
2926{ 2962{
2927 struct file *file = iocb->ki_filp; 2963 struct file *file = iocb->ki_filp;
2928 struct inode *inode = file->f_mapping->host; 2964 struct inode *inode = file->f_mapping->host;
2929 unsigned long limit = rlimit(RLIMIT_FSIZE); 2965 loff_t count;
2930 loff_t pos; 2966 int ret;
2931 2967
2932 if (!iov_iter_count(from)) 2968 if (!iov_iter_count(from))
2933 return 0; 2969 return 0;
@@ -2936,40 +2972,15 @@ inline ssize_t generic_write_checks(struct kiocb *iocb, struct iov_iter *from)
2936 if (iocb->ki_flags & IOCB_APPEND) 2972 if (iocb->ki_flags & IOCB_APPEND)
2937 iocb->ki_pos = i_size_read(inode); 2973 iocb->ki_pos = i_size_read(inode);
2938 2974
2939 pos = iocb->ki_pos;
2940
2941 if ((iocb->ki_flags & IOCB_NOWAIT) && !(iocb->ki_flags & IOCB_DIRECT)) 2975 if ((iocb->ki_flags & IOCB_NOWAIT) && !(iocb->ki_flags & IOCB_DIRECT))
2942 return -EINVAL; 2976 return -EINVAL;
2943 2977
2944 if (limit != RLIM_INFINITY) { 2978 count = iov_iter_count(from);
2945 if (iocb->ki_pos >= limit) { 2979 ret = generic_write_check_limits(file, iocb->ki_pos, &count);
2946 send_sig(SIGXFSZ, current, 0); 2980 if (ret)
2947 return -EFBIG; 2981 return ret;
2948 }
2949 iov_iter_truncate(from, limit - (unsigned long)pos);
2950 }
2951
2952 /*
2953 * LFS rule
2954 */
2955 if (unlikely(pos + iov_iter_count(from) > MAX_NON_LFS &&
2956 !(file->f_flags & O_LARGEFILE))) {
2957 if (pos >= MAX_NON_LFS)
2958 return -EFBIG;
2959 iov_iter_truncate(from, MAX_NON_LFS - (unsigned long)pos);
2960 }
2961
2962 /*
2963 * Are we about to exceed the fs block limit ?
2964 *
2965 * If we have written data it becomes a short write. If we have
2966 * exceeded without writing data we send a signal and return EFBIG.
2967 * Linus frestrict idea will clean these up nicely..
2968 */
2969 if (unlikely(pos >= inode->i_sb->s_maxbytes))
2970 return -EFBIG;
2971 2982
2972 iov_iter_truncate(from, inode->i_sb->s_maxbytes - pos); 2983 iov_iter_truncate(from, count);
2973 return iov_iter_count(from); 2984 return iov_iter_count(from);
2974} 2985}
2975EXPORT_SYMBOL(generic_write_checks); 2986EXPORT_SYMBOL(generic_write_checks);
@@ -2991,6 +3002,7 @@ int generic_remap_checks(struct file *file_in, loff_t pos_in,
2991 uint64_t bcount; 3002 uint64_t bcount;
2992 loff_t size_in, size_out; 3003 loff_t size_in, size_out;
2993 loff_t bs = inode_out->i_sb->s_blocksize; 3004 loff_t bs = inode_out->i_sb->s_blocksize;
3005 int ret;
2994 3006
2995 /* The start of both ranges must be aligned to an fs block. */ 3007 /* The start of both ranges must be aligned to an fs block. */
2996 if (!IS_ALIGNED(pos_in, bs) || !IS_ALIGNED(pos_out, bs)) 3008 if (!IS_ALIGNED(pos_in, bs) || !IS_ALIGNED(pos_out, bs))
@@ -3014,6 +3026,14 @@ int generic_remap_checks(struct file *file_in, loff_t pos_in,
3014 return -EINVAL; 3026 return -EINVAL;
3015 count = min(count, size_in - (uint64_t)pos_in); 3027 count = min(count, size_in - (uint64_t)pos_in);
3016 3028
3029 ret = generic_access_check_limits(file_in, pos_in, &count);
3030 if (ret)
3031 return ret;
3032
3033 ret = generic_write_check_limits(file_out, pos_out, &count);
3034 if (ret)
3035 return ret;
3036
3017 /* 3037 /*
3018 * If the user wanted us to link to the infile's EOF, round up to the 3038 * If the user wanted us to link to the infile's EOF, round up to the
3019 * next block boundary for this check. 3039 * next block boundary for this check.