Skip to content

Commit 9e65bfc

Browse files
boryaskdave
authored andcommitted
btrfs: fix qgroup_free_reserved_data int overflow
The reserved data counter and input parameter is a u64, but we inadvertently accumulate it in an int. Overflowing that int results in freeing the wrong amount of data and breaking reserve accounting. Unfortunately, this overflow rot spreads from there, as the qgroup release/free functions rely on returning an int to take advantage of negative values for error codes. Therefore, the full fix is to return the "released" or "freed" amount by a u64 argument and to return 0 or negative error code via the return value. Most of the call sites simply ignore the return value, though some of them handle the error and count the returned bytes. Change all of them accordingly. CC: [email protected] # 6.1+ Reviewed-by: Qu Wenruo <[email protected]> Signed-off-by: Boris Burkov <[email protected]> Reviewed-by: David Sterba <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent f63e116 commit 9e65bfc

File tree

6 files changed

+31
-25
lines changed

6 files changed

+31
-25
lines changed

fs/btrfs/delalloc-space.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ void btrfs_free_reserved_data_space(struct btrfs_inode *inode,
199199
start = round_down(start, fs_info->sectorsize);
200200

201201
btrfs_free_reserved_data_space_noquota(fs_info, len);
202-
btrfs_qgroup_free_data(inode, reserved, start, len);
202+
btrfs_qgroup_free_data(inode, reserved, start, len, NULL);
203203
}
204204

205205
/*

fs/btrfs/file.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3190,7 +3190,7 @@ static long btrfs_fallocate(struct file *file, int mode,
31903190
qgroup_reserved -= range->len;
31913191
} else if (qgroup_reserved > 0) {
31923192
btrfs_qgroup_free_data(BTRFS_I(inode), data_reserved,
3193-
range->start, range->len);
3193+
range->start, range->len, NULL);
31943194
qgroup_reserved -= range->len;
31953195
}
31963196
list_del(&range->list);

fs/btrfs/inode.c

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,7 @@ static noinline int cow_file_range_inline(struct btrfs_inode *inode, u64 size,
688688
* And at reserve time, it's always aligned to page size, so
689689
* just free one page here.
690690
*/
691-
btrfs_qgroup_free_data(inode, NULL, 0, PAGE_SIZE);
691+
btrfs_qgroup_free_data(inode, NULL, 0, PAGE_SIZE, NULL);
692692
btrfs_free_path(path);
693693
btrfs_end_transaction(trans);
694694
return ret;
@@ -5132,7 +5132,7 @@ static void evict_inode_truncate_pages(struct inode *inode)
51325132
*/
51335133
if (state_flags & EXTENT_DELALLOC)
51345134
btrfs_qgroup_free_data(BTRFS_I(inode), NULL, start,
5135-
end - start + 1);
5135+
end - start + 1, NULL);
51365136

51375137
clear_extent_bit(io_tree, start, end,
51385138
EXTENT_CLEAR_ALL_BITS | EXTENT_DO_ACCOUNTING,
@@ -8055,7 +8055,7 @@ static void btrfs_invalidate_folio(struct folio *folio, size_t offset,
80558055
* reserved data space.
80568056
* Since the IO will never happen for this page.
80578057
*/
8058-
btrfs_qgroup_free_data(inode, NULL, cur, range_end + 1 - cur);
8058+
btrfs_qgroup_free_data(inode, NULL, cur, range_end + 1 - cur, NULL);
80598059
if (!inode_evicting) {
80608060
clear_extent_bit(tree, cur, range_end, EXTENT_LOCKED |
80618061
EXTENT_DELALLOC | EXTENT_UPTODATE |
@@ -9487,7 +9487,7 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent(
94879487
struct btrfs_path *path;
94889488
u64 start = ins->objectid;
94899489
u64 len = ins->offset;
9490-
int qgroup_released;
9490+
u64 qgroup_released = 0;
94919491
int ret;
94929492

94939493
memset(&stack_fi, 0, sizeof(stack_fi));
@@ -9500,9 +9500,9 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent(
95009500
btrfs_set_stack_file_extent_compression(&stack_fi, BTRFS_COMPRESS_NONE);
95019501
/* Encryption and other encoding is reserved and all 0 */
95029502

9503-
qgroup_released = btrfs_qgroup_release_data(inode, file_offset, len);
9504-
if (qgroup_released < 0)
9505-
return ERR_PTR(qgroup_released);
9503+
ret = btrfs_qgroup_release_data(inode, file_offset, len, &qgroup_released);
9504+
if (ret < 0)
9505+
return ERR_PTR(ret);
95069506

95079507
if (trans) {
95089508
ret = insert_reserved_file_extent(trans, inode,
@@ -10397,7 +10397,7 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from,
1039710397
btrfs_delalloc_release_metadata(inode, disk_num_bytes, ret < 0);
1039810398
out_qgroup_free_data:
1039910399
if (ret < 0)
10400-
btrfs_qgroup_free_data(inode, data_reserved, start, num_bytes);
10400+
btrfs_qgroup_free_data(inode, data_reserved, start, num_bytes, NULL);
1040110401
out_free_data_space:
1040210402
/*
1040310403
* If btrfs_reserve_extent() succeeded, then we already decremented

fs/btrfs/ordered-data.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,19 +152,20 @@ static struct btrfs_ordered_extent *alloc_ordered_extent(
152152
{
153153
struct btrfs_ordered_extent *entry;
154154
int ret;
155+
u64 qgroup_rsv = 0;
155156

156157
if (flags &
157158
((1 << BTRFS_ORDERED_NOCOW) | (1 << BTRFS_ORDERED_PREALLOC))) {
158159
/* For nocow write, we can release the qgroup rsv right now */
159-
ret = btrfs_qgroup_free_data(inode, NULL, file_offset, num_bytes);
160+
ret = btrfs_qgroup_free_data(inode, NULL, file_offset, num_bytes, &qgroup_rsv);
160161
if (ret < 0)
161162
return ERR_PTR(ret);
162163
} else {
163164
/*
164165
* The ordered extent has reserved qgroup space, release now
165166
* and pass the reserved number for qgroup_record to free.
166167
*/
167-
ret = btrfs_qgroup_release_data(inode, file_offset, num_bytes);
168+
ret = btrfs_qgroup_release_data(inode, file_offset, num_bytes, &qgroup_rsv);
168169
if (ret < 0)
169170
return ERR_PTR(ret);
170171
}
@@ -182,7 +183,7 @@ static struct btrfs_ordered_extent *alloc_ordered_extent(
182183
entry->inode = igrab(&inode->vfs_inode);
183184
entry->compress_type = compress_type;
184185
entry->truncated_len = (u64)-1;
185-
entry->qgroup_rsv = ret;
186+
entry->qgroup_rsv = qgroup_rsv;
186187
entry->flags = flags;
187188
refcount_set(&entry->refs, 1);
188189
init_waitqueue_head(&entry->wait);

fs/btrfs/qgroup.c

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4057,13 +4057,14 @@ int btrfs_qgroup_reserve_data(struct btrfs_inode *inode,
40574057

40584058
/* Free ranges specified by @reserved, normally in error path */
40594059
static int qgroup_free_reserved_data(struct btrfs_inode *inode,
4060-
struct extent_changeset *reserved, u64 start, u64 len)
4060+
struct extent_changeset *reserved,
4061+
u64 start, u64 len, u64 *freed_ret)
40614062
{
40624063
struct btrfs_root *root = inode->root;
40634064
struct ulist_node *unode;
40644065
struct ulist_iterator uiter;
40654066
struct extent_changeset changeset;
4066-
int freed = 0;
4067+
u64 freed = 0;
40674068
int ret;
40684069

40694070
extent_changeset_init(&changeset);
@@ -4104,15 +4105,17 @@ static int qgroup_free_reserved_data(struct btrfs_inode *inode,
41044105
}
41054106
btrfs_qgroup_free_refroot(root->fs_info, root->root_key.objectid, freed,
41064107
BTRFS_QGROUP_RSV_DATA);
4107-
ret = freed;
4108+
if (freed_ret)
4109+
*freed_ret = freed;
4110+
ret = 0;
41084111
out:
41094112
extent_changeset_release(&changeset);
41104113
return ret;
41114114
}
41124115

41134116
static int __btrfs_qgroup_release_data(struct btrfs_inode *inode,
41144117
struct extent_changeset *reserved, u64 start, u64 len,
4115-
int free)
4118+
u64 *released, int free)
41164119
{
41174120
struct extent_changeset changeset;
41184121
int trace_op = QGROUP_RELEASE;
@@ -4128,7 +4131,7 @@ static int __btrfs_qgroup_release_data(struct btrfs_inode *inode,
41284131
/* In release case, we shouldn't have @reserved */
41294132
WARN_ON(!free && reserved);
41304133
if (free && reserved)
4131-
return qgroup_free_reserved_data(inode, reserved, start, len);
4134+
return qgroup_free_reserved_data(inode, reserved, start, len, released);
41324135
extent_changeset_init(&changeset);
41334136
ret = clear_record_extent_bits(&inode->io_tree, start, start + len -1,
41344137
EXTENT_QGROUP_RESERVED, &changeset);
@@ -4143,7 +4146,8 @@ static int __btrfs_qgroup_release_data(struct btrfs_inode *inode,
41434146
btrfs_qgroup_free_refroot(inode->root->fs_info,
41444147
inode->root->root_key.objectid,
41454148
changeset.bytes_changed, BTRFS_QGROUP_RSV_DATA);
4146-
ret = changeset.bytes_changed;
4149+
if (released)
4150+
*released = changeset.bytes_changed;
41474151
out:
41484152
extent_changeset_release(&changeset);
41494153
return ret;
@@ -4162,9 +4166,10 @@ static int __btrfs_qgroup_release_data(struct btrfs_inode *inode,
41624166
* NOTE: This function may sleep for memory allocation.
41634167
*/
41644168
int btrfs_qgroup_free_data(struct btrfs_inode *inode,
4165-
struct extent_changeset *reserved, u64 start, u64 len)
4169+
struct extent_changeset *reserved,
4170+
u64 start, u64 len, u64 *freed)
41664171
{
4167-
return __btrfs_qgroup_release_data(inode, reserved, start, len, 1);
4172+
return __btrfs_qgroup_release_data(inode, reserved, start, len, freed, 1);
41684173
}
41694174

41704175
/*
@@ -4182,9 +4187,9 @@ int btrfs_qgroup_free_data(struct btrfs_inode *inode,
41824187
*
41834188
* NOTE: This function may sleep for memory allocation.
41844189
*/
4185-
int btrfs_qgroup_release_data(struct btrfs_inode *inode, u64 start, u64 len)
4190+
int btrfs_qgroup_release_data(struct btrfs_inode *inode, u64 start, u64 len, u64 *released)
41864191
{
4187-
return __btrfs_qgroup_release_data(inode, NULL, start, len, 0);
4192+
return __btrfs_qgroup_release_data(inode, NULL, start, len, released, 0);
41884193
}
41894194

41904195
static void add_root_meta_rsv(struct btrfs_root *root, int num_bytes,

fs/btrfs/qgroup.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,10 +358,10 @@ int btrfs_verify_qgroup_counts(struct btrfs_fs_info *fs_info, u64 qgroupid,
358358
/* New io_tree based accurate qgroup reserve API */
359359
int btrfs_qgroup_reserve_data(struct btrfs_inode *inode,
360360
struct extent_changeset **reserved, u64 start, u64 len);
361-
int btrfs_qgroup_release_data(struct btrfs_inode *inode, u64 start, u64 len);
361+
int btrfs_qgroup_release_data(struct btrfs_inode *inode, u64 start, u64 len, u64 *released);
362362
int btrfs_qgroup_free_data(struct btrfs_inode *inode,
363363
struct extent_changeset *reserved, u64 start,
364-
u64 len);
364+
u64 len, u64 *freed);
365365
int btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes,
366366
enum btrfs_qgroup_rsv_type type, bool enforce);
367367
int __btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes,

0 commit comments

Comments
 (0)