btrfs: trim: fix underflow in trim length to prevent access beyond device boundary
commit c57dd1f2f6a7cd1bb61802344f59ccdc5278c983 upstream [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 929be17a9b49 ("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: https://github.com/kdave/btrfs-progs/issues/282 Fixes: 929be17a9b49 ("btrfs: Switch btrfs_trim_free_extents to find_first_clear_extent_bit") CC: stable@vger.kernel.org # 5.4+ Signed-off-by: Qu Wenruo <wqu@suse.com> Reviewed-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: David Sterba <dsterba@suse.com> [sudip: adjust context and use extent_io.h] Signed-off-by: Sudip Mukherjee <sudipm.mukherjee@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
1d11ed122f
commit
e21d630a2c
@ -32,6 +32,7 @@
|
||||
#include "block-rsv.h"
|
||||
#include "delalloc-space.h"
|
||||
#include "block-group.h"
|
||||
#include "rcu-string.h"
|
||||
|
||||
#undef SCRAMBLE_DELAYED_REFS
|
||||
|
||||
@ -5618,6 +5619,19 @@ static int btrfs_trim_free_extents(struct btrfs_device *device, u64 *trimmed)
|
||||
&start, &end,
|
||||
CHUNK_TRIMMED | CHUNK_ALLOCATED);
|
||||
|
||||
/* Check if there are any CHUNK_* bits left */
|
||||
if (start > device->total_bytes) {
|
||||
WARN_ON(IS_ENABLED(CONFIG_BTRFS_DEBUG));
|
||||
btrfs_warn_in_rcu(fs_info,
|
||||
"ignoring attempt to trim beyond device size: offset %llu length %llu device %s device size %llu",
|
||||
start, end - start + 1,
|
||||
rcu_str_deref(device->name),
|
||||
device->total_bytes);
|
||||
mutex_unlock(&fs_info->chunk_mutex);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Ensure we skip the reserved area in the first 1M */
|
||||
start = max_t(u64, start, SZ_1M);
|
||||
|
||||
|
@ -35,6 +35,8 @@
|
||||
*/
|
||||
#define CHUNK_ALLOCATED EXTENT_DIRTY
|
||||
#define CHUNK_TRIMMED EXTENT_DEFRAG
|
||||
#define CHUNK_STATE_MASK (CHUNK_ALLOCATED | \
|
||||
CHUNK_TRIMMED)
|
||||
|
||||
/*
|
||||
* flags for bio submission. The high bits indicate the compression
|
||||
|
@ -4908,6 +4908,10 @@ again:
|
||||
}
|
||||
|
||||
mutex_lock(&fs_info->chunk_mutex);
|
||||
/* Clear all state bits beyond the shrunk device size */
|
||||
clear_extent_bits(&device->alloc_state, new_size, (u64)-1,
|
||||
CHUNK_STATE_MASK);
|
||||
|
||||
btrfs_device_set_disk_total_bytes(device, new_size);
|
||||
if (list_empty(&device->post_commit_list))
|
||||
list_add_tail(&device->post_commit_list,
|
||||
|
Loading…
x
Reference in New Issue
Block a user