Path: csiph.com!1.us.feeder.erje.net!feeder.erje.net!2.eu.feeder.erje.net!newsfeed.fsmpi.rwth-aachen.de!newsfeed.straub-nv.de!news.unit0.net!news.panservice.it!bofh.it!news.nic.it!robomod From: "Darrick J. Wong" Newsgroups: linux.kernel Subject: [PATCH] vfs: fix isize/pos/len checks for reflink & dedupe Date: Mon, 19 Dec 2016 04:20:01 +0100 Message-ID: X-Original-To: Al Viro MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline User-Agent: Mutt/1.5.24 (2015-08-30) X-Source-IP: aserv0022.oracle.com [141.146.126.234] Sender: robomod@news.nic.it List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Approved: robomod@news.nic.it Lines: 91 Organization: linux.* mail to news gateway X-Original-Cc: Linus Torvalds , Linux Kernel Mailing List , linux-fsdevel X-Original-Date: Sun, 18 Dec 2016 19:15:01 -0800 X-Original-Message-ID: <20161219031501.GB7311@birch.djwong.org> X-Original-Sender: linux-kernel-owner@vger.kernel.org Xref: csiph.com linux.kernel:1544313 Strengthen the checking of pos/len vs. i_size, clarify the return values for the clone prep function, and remove pointless code. Signed-off-by: Darrick J. WOng --- fs/ocfs2/refcounttree.c | 2 +- fs/read_write.c | 18 +++++++++++------- fs/xfs/xfs_reflink.c | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index b18465e..21723dd 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -4835,7 +4835,7 @@ int ocfs2_reflink_remap_range(struct file *file_in, ret = vfs_clone_file_prep_inodes(inode_in, pos_in, inode_out, pos_out, &len, is_dedupe); - if (ret || len == 0) + if (ret <= 0) goto out_unlock; /* Lock out changes to the allocation maps and remap. */ diff --git a/fs/read_write.c b/fs/read_write.c index dbf3f7f..434da26 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -1671,6 +1671,9 @@ static int clone_verify_area(struct file *file, loff_t pos, u64 len, bool write) * Check that the two inodes are eligible for cloning, the ranges make * sense, and then flush all dirty data. Caller must ensure that the * inodes have been locked against any other modifications. + * + * Returns: 0 for "nothing to clone", 1 for "something to clone", or + * the usual negative error code. */ int vfs_clone_file_prep_inodes(struct inode *inode_in, loff_t pos_in, struct inode *inode_out, loff_t pos_out, @@ -1697,17 +1700,15 @@ int vfs_clone_file_prep_inodes(struct inode *inode_in, loff_t pos_in, /* Are we going all the way to the end? */ isize = i_size_read(inode_in); - if (isize == 0) { - *len = 0; + if (isize == 0) return 0; - } /* Zero length dedupe exits immediately; reflink goes to EOF. */ if (*len == 0) { - if (is_dedupe) { - *len = 0; + if (is_dedupe || pos_in == isize) return 0; - } + else if (pos_in > isize) + return -EINVAL; *len = isize - pos_in; } @@ -1771,7 +1772,7 @@ int vfs_clone_file_prep_inodes(struct inode *inode_in, loff_t pos_in, return -EBADE; } - return 0; + return 1; } EXPORT_SYMBOL(vfs_clone_file_prep_inodes); @@ -1958,6 +1959,9 @@ int vfs_dedupe_file_range(struct file *file, struct file_dedupe_range *same) goto out; ret = 0; + if (off + len > i_size_read(src)) + return -EINVAL; + /* pre-format output fields to sane values */ for (i = 0; i < count; i++) { same->info[i].bytes_deduped = 0ULL; diff --git a/fs/xfs/xfs_reflink.c b/fs/xfs/xfs_reflink.c index 43c0042..2a8ea32 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -1215,7 +1215,7 @@ xfs_reflink_remap_range( ret = vfs_clone_file_prep_inodes(inode_in, pos_in, inode_out, pos_out, &len, is_dedupe); - if (ret || len == 0) + if (ret <= 0) goto out_unlock; trace_xfs_reflink_remap_range(src, pos_in, len, dest, pos_out);