mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 03:40:35 +09:00
Btrfs: fix clone vs chattr NODATASUM race
commitb5c40d598fupstream. 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:0e7b824c4e("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> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
4f65ebcffa
commit
52ea25b2b8
@@ -3887,11 +3887,6 @@ static noinline int btrfs_clone_files(struct file *file, struct file *file_src,
|
||||
src->i_sb != inode->i_sb)
|
||||
return -EXDEV;
|
||||
|
||||
/* don't make the dst file partly checksummed */
|
||||
if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) !=
|
||||
(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM))
|
||||
return -EINVAL;
|
||||
|
||||
if (S_ISDIR(src->i_mode) || S_ISDIR(inode->i_mode))
|
||||
return -EISDIR;
|
||||
|
||||
@@ -3901,6 +3896,13 @@ static noinline int btrfs_clone_files(struct file *file, struct file *file_src,
|
||||
inode_lock(src);
|
||||
}
|
||||
|
||||
/* don't make the dst file partly checksummed */
|
||||
if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) !=
|
||||
(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) {
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/* determine range to clone */
|
||||
ret = -EINVAL;
|
||||
if (off + len > src->i_size || off + len < off)
|
||||
|
||||
Reference in New Issue
Block a user