Skip to content

Commit d4e2049

Browse files
adam900710kdave
authored andcommitted
btrfs: qgroup: Fix reserved data space leak if we have multiple reserve calls
[BUG] The following script can cause btrfs qgroup data space leak: mkfs.btrfs -f $dev mount $dev -o nospace_cache $mnt btrfs subv create $mnt/subv btrfs quota en $mnt btrfs quota rescan -w $mnt btrfs qgroup limit 128m $mnt/subv for (( i = 0; i < 3; i++)); do # Create 3 64M holes for latter fallocate to fail truncate -s 192m $mnt/subv/file xfs_io -c "pwrite 64m 4k" $mnt/subv/file > /dev/null xfs_io -c "pwrite 128m 4k" $mnt/subv/file > /dev/null sync # it's supposed to fail, and each failure will leak at least 64M # data space xfs_io -f -c "falloc 0 192m" $mnt/subv/file &> /dev/null rm $mnt/subv/file sync done # Shouldn't fail after we removed the file xfs_io -f -c "falloc 0 64m" $mnt/subv/file [CAUSE] Btrfs qgroup data reserve code allow multiple reservations to happen on a single extent_changeset: E.g: btrfs_qgroup_reserve_data(inode, &data_reserved, 0, SZ_1M); btrfs_qgroup_reserve_data(inode, &data_reserved, SZ_1M, SZ_2M); btrfs_qgroup_reserve_data(inode, &data_reserved, 0, SZ_4M); Btrfs qgroup code has its internal tracking to make sure we don't double-reserve in above example. The only pattern utilizing this feature is in the main while loop of btrfs_fallocate() function. However btrfs_qgroup_reserve_data()'s error handling has a bug in that on error it clears all ranges in the io_tree with EXTENT_QGROUP_RESERVED flag but doesn't free previously reserved bytes. This bug has a two fold effect: - Clearing EXTENT_QGROUP_RESERVED ranges This is the correct behavior, but it prevents btrfs_qgroup_check_reserved_leak() to catch the leakage as the detector is purely EXTENT_QGROUP_RESERVED flag based. - Leak the previously reserved data bytes. The bug manifests when N calls to btrfs_qgroup_reserve_data are made and the last one fails, leaking space reserved in the previous ones. [FIX] Also free previously reserved data bytes when btrfs_qgroup_reserve_data fails. Fixes: 5247255 ("btrfs: qgroup: Introduce btrfs_qgroup_reserve_data function") CC: [email protected] # 4.4+ Signed-off-by: Qu Wenruo <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent bab32fc commit d4e2049

File tree

1 file changed

+3
-0
lines changed

1 file changed

+3
-0
lines changed

fs/btrfs/qgroup.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3442,6 +3442,9 @@ int btrfs_qgroup_reserve_data(struct inode *inode,
34423442
while ((unode = ulist_next(&reserved->range_changed, &uiter)))
34433443
clear_extent_bit(&BTRFS_I(inode)->io_tree, unode->val,
34443444
unode->aux, EXTENT_QGROUP_RESERVED, 0, 0, NULL);
3445+
/* Also free data bytes of already reserved one */
3446+
btrfs_qgroup_free_refroot(root->fs_info, root->root_key.objectid,
3447+
orig_reserved, BTRFS_QGROUP_RSV_DATA);
34453448
extent_changeset_release(reserved);
34463449
return ret;
34473450
}

0 commit comments

Comments
 (0)