Skip to content

Commit c57dd1f

Browse files
adam900710kdave
authored andcommitted
btrfs: trim: fix underflow in trim length to prevent access beyond device boundary
[BUG] The following script can lead to tons of beyond device boundary access: mkfs.btrfs -f $dev -b 10G mount $dev $mnt trimfs $mnt btrfs filesystem resize 1:-1G $mnt trimfs $mnt [CAUSE] Since commit 929be17 ("btrfs: Switch btrfs_trim_free_extents to find_first_clear_extent_bit"), we try to avoid trimming ranges that's already trimmed. So we check device->alloc_state by finding the first range which doesn't have CHUNK_TRIMMED and CHUNK_ALLOCATED not set. But if we shrunk the device, that bits are not cleared, thus we could easily got a range starts beyond the shrunk device size. This results the returned @start and @EnD are all beyond device size, then we call "end = min(end, device->total_bytes -1);" making @EnD smaller than device size. Then finally we goes "len = end - start + 1", totally underflow the result, and lead to the beyond-device-boundary access. [FIX] This patch will fix the problem in two ways: - Clear CHUNK_TRIMMED | CHUNK_ALLOCATED bits when shrinking device This is the root fix - Add extra safety check when trimming free device extents We check and warn if the returned range is already beyond current device. Link: kdave/btrfs-progs#282 Fixes: 929be17 ("btrfs: Switch btrfs_trim_free_extents to find_first_clear_extent_bit") CC: [email protected] # 5.4+ Signed-off-by: Qu Wenruo <[email protected]> Reviewed-by: Filipe Manana <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 881a3a1 commit c57dd1f

File tree

3 files changed

+20
-0
lines changed

3 files changed

+20
-0
lines changed

fs/btrfs/extent-io-tree.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ struct io_failure_record;
3434
*/
3535
#define CHUNK_ALLOCATED EXTENT_DIRTY
3636
#define CHUNK_TRIMMED EXTENT_DEFRAG
37+
#define CHUNK_STATE_MASK (CHUNK_ALLOCATED | \
38+
CHUNK_TRIMMED)
3739

3840
enum {
3941
IO_TREE_FS_PINNED_EXTENTS,

fs/btrfs/extent-tree.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "delalloc-space.h"
3434
#include "block-group.h"
3535
#include "discard.h"
36+
#include "rcu-string.h"
3637

3738
#undef SCRAMBLE_DELAYED_REFS
3839

@@ -5668,6 +5669,19 @@ static int btrfs_trim_free_extents(struct btrfs_device *device, u64 *trimmed)
56685669
&start, &end,
56695670
CHUNK_TRIMMED | CHUNK_ALLOCATED);
56705671

5672+
/* Check if there are any CHUNK_* bits left */
5673+
if (start > device->total_bytes) {
5674+
WARN_ON(IS_ENABLED(CONFIG_BTRFS_DEBUG));
5675+
btrfs_warn_in_rcu(fs_info,
5676+
"ignoring attempt to trim beyond device size: offset %llu length %llu device %s device size %llu",
5677+
start, end - start + 1,
5678+
rcu_str_deref(device->name),
5679+
device->total_bytes);
5680+
mutex_unlock(&fs_info->chunk_mutex);
5681+
ret = 0;
5682+
break;
5683+
}
5684+
56715685
/* Ensure we skip the reserved area in the first 1M */
56725686
start = max_t(u64, start, SZ_1M);
56735687

fs/btrfs/volumes.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4720,6 +4720,10 @@ int btrfs_shrink_device(struct btrfs_device *device, u64 new_size)
47204720
}
47214721

47224722
mutex_lock(&fs_info->chunk_mutex);
4723+
/* Clear all state bits beyond the shrunk device size */
4724+
clear_extent_bits(&device->alloc_state, new_size, (u64)-1,
4725+
CHUNK_STATE_MASK);
4726+
47234727
btrfs_device_set_disk_total_bytes(device, new_size);
47244728
if (list_empty(&device->post_commit_list))
47254729
list_add_tail(&device->post_commit_list,

0 commit comments

Comments
 (0)