Skip to content

Commit b5c40d5

Browse files
osandovkdave
authored andcommitted
Btrfs: fix clone vs chattr NODATASUM race
In btrfs_clone_files(), we must check the NODATASUM flag while the inodes are locked. Otherwise, it's possible that btrfs_ioctl_setflags() will change the flags after we check and we can end up with a party checksummed file. The race window is only a few instructions in size, between the if and the locks which is: 3834 if (S_ISDIR(src->i_mode) || S_ISDIR(inode->i_mode)) 3835 return -EISDIR; where the setflags must be run and toggle the NODATASUM flag (provided the file size is 0). The clone will block on the inode lock, segflags takes the inode lock, changes flags, releases log and clone continues. Not impossible but still needs a lot of bad luck to hit unintentionally. Fixes: 0e7b824 ("Btrfs: don't make a file partly checksummed through file clone") CC: stable@vger.kernel.org # 4.4+ Signed-off-by: Omar Sandoval <osandov@fb.com> Reviewed-by: Nikolay Borisov <nborisov@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> [ update changelog ] Signed-off-by: David Sterba <dsterba@suse.com>
1 parent b89311e commit b5c40d5

File tree

1 file changed

+7
-5
lines changed

1 file changed

+7
-5
lines changed

fs/btrfs/ioctl.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3808,11 +3808,6 @@ static noinline int btrfs_clone_files(struct file *file, struct file *file_src,
38083808
src->i_sb != inode->i_sb)
38093809
return -EXDEV;
38103810

3811-
/* don't make the dst file partly checksummed */
3812-
if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) !=
3813-
(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM))
3814-
return -EINVAL;
3815-
38163811
if (S_ISDIR(src->i_mode) || S_ISDIR(inode->i_mode))
38173812
return -EISDIR;
38183813

@@ -3822,6 +3817,13 @@ static noinline int btrfs_clone_files(struct file *file, struct file *file_src,
38223817
inode_lock(src);
38233818
}
38243819

3820+
/* don't make the dst file partly checksummed */
3821+
if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) !=
3822+
(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) {
3823+
ret = -EINVAL;
3824+
goto out_unlock;
3825+
}
3826+
38253827
/* determine range to clone */
38263828
ret = -EINVAL;
38273829
if (off + len > src->i_size || off + len < off)

0 commit comments

Comments
 (0)