btrfs-progs: add extra chunk alignment checks
Recently we had a scrub use-after-free caused by unaligned chunk length, although the fix was submitted, we may want to do extra checks for a chunk's alignment. This patch adds such check for the starting bytenr and length of a chunk, to make sure they are properly aligned to 64K stripe boundary. By default, the check only leads to a warning but is not treated as an error, as we expect kernel to handle such unalignment without any problem. But if the new debug environmental variable, BTRFS_PROGS_DEBUG_STRICT_CHUNK_ALIGNMENT, is specified, then we will treat it as an error. So that we can detect unexpected chunks from btrfs-progs, and fix them before reaching the end users. Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
136c58617c
commit
6dfc69104b
|
@ -78,6 +78,7 @@ struct chunk_record {
|
||||||
u32 io_align;
|
u32 io_align;
|
||||||
u32 io_width;
|
u32 io_width;
|
||||||
u32 sector_size;
|
u32 sector_size;
|
||||||
|
bool unaligned;
|
||||||
struct stripe stripes[0];
|
struct stripe stripes[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
20
check/main.c
20
check/main.c
|
@ -5265,6 +5265,9 @@ struct chunk_record *btrfs_new_chunk_record(struct extent_buffer *leaf,
|
||||||
rec->num_stripes = num_stripes;
|
rec->num_stripes = num_stripes;
|
||||||
rec->sub_stripes = btrfs_chunk_sub_stripes(leaf, ptr);
|
rec->sub_stripes = btrfs_chunk_sub_stripes(leaf, ptr);
|
||||||
|
|
||||||
|
if (!IS_ALIGNED(rec->cache.start, BTRFS_STRIPE_LEN) ||
|
||||||
|
!IS_ALIGNED(rec->cache.size, BTRFS_STRIPE_LEN))
|
||||||
|
rec->unaligned = true;
|
||||||
for (i = 0; i < rec->num_stripes; ++i) {
|
for (i = 0; i < rec->num_stripes; ++i) {
|
||||||
rec->stripes[i].devid =
|
rec->stripes[i].devid =
|
||||||
btrfs_stripe_devid_nr(leaf, ptr, i);
|
btrfs_stripe_devid_nr(leaf, ptr, i);
|
||||||
|
@ -8386,6 +8389,7 @@ int check_chunks(struct cache_tree *chunk_cache,
|
||||||
struct chunk_record *chunk_rec;
|
struct chunk_record *chunk_rec;
|
||||||
struct block_group_record *bg_rec;
|
struct block_group_record *bg_rec;
|
||||||
struct device_extent_record *dext_rec;
|
struct device_extent_record *dext_rec;
|
||||||
|
bool strict_alignment = get_env_bool("BTRFS_DEBUG_STRICT_CHUNK_ALIGNMENT");
|
||||||
int err;
|
int err;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
|
@ -8393,6 +8397,22 @@ int check_chunks(struct cache_tree *chunk_cache,
|
||||||
while (chunk_item) {
|
while (chunk_item) {
|
||||||
chunk_rec = container_of(chunk_item, struct chunk_record,
|
chunk_rec = container_of(chunk_item, struct chunk_record,
|
||||||
cache);
|
cache);
|
||||||
|
if (chunk_rec->unaligned && !silent) {
|
||||||
|
if (strict_alignment) {
|
||||||
|
error(
|
||||||
|
"chunk[%llu %llu) is not fully aligned to BTRFS_STRIPE_LEN (%u)",
|
||||||
|
chunk_rec->cache.start,
|
||||||
|
chunk_rec->cache.start + chunk_rec->cache.size,
|
||||||
|
BTRFS_STRIPE_LEN);
|
||||||
|
ret = -EINVAL;
|
||||||
|
} else {
|
||||||
|
warning(
|
||||||
|
"chunk[%llu %llu) is not fully aligned to BTRFS_STRIPE_LEN (%u)",
|
||||||
|
chunk_rec->cache.start,
|
||||||
|
chunk_rec->cache.start + chunk_rec->cache.size,
|
||||||
|
BTRFS_STRIPE_LEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
err = check_chunk_refs(chunk_rec, block_group_cache,
|
err = check_chunk_refs(chunk_rec, block_group_cache,
|
||||||
dev_extent_cache, silent);
|
dev_extent_cache, silent);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
|
|
@ -4719,6 +4719,17 @@ static int check_chunk_item(struct extent_buffer *eb, int slot)
|
||||||
chunk = btrfs_item_ptr(eb, slot, struct btrfs_chunk);
|
chunk = btrfs_item_ptr(eb, slot, struct btrfs_chunk);
|
||||||
length = btrfs_chunk_length(eb, chunk);
|
length = btrfs_chunk_length(eb, chunk);
|
||||||
chunk_end = chunk_key.offset + length;
|
chunk_end = chunk_key.offset + length;
|
||||||
|
if (!IS_ALIGNED(chunk_key.offset, BTRFS_STRIPE_LEN) ||
|
||||||
|
!IS_ALIGNED(length, BTRFS_STRIPE_LEN)) {
|
||||||
|
if (get_env_bool("BTRFS_PROGS_DEBUG_STRICT_CHUNK_ALIGNMENT")) {
|
||||||
|
error("chunk[%llu %llu) is not fully aligned to BTRFS_STRIPE_LEN (%u)",
|
||||||
|
chunk_key.offset, length, BTRFS_STRIPE_LEN);
|
||||||
|
err |= BYTES_UNALIGNED;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
warning("chunk[%llu %llu) is not fully aligned to BTRFS_STRIPE_LEN (%u)",
|
||||||
|
chunk_key.offset, length, BTRFS_STRIPE_LEN);
|
||||||
|
}
|
||||||
ret = btrfs_check_chunk_valid(eb, chunk, chunk_key.offset);
|
ret = btrfs_check_chunk_valid(eb, chunk, chunk_key.offset);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error("chunk[%llu %llu) is invalid", chunk_key.offset,
|
error("chunk[%llu %llu) is invalid", chunk_key.offset,
|
||||||
|
|
|
@ -956,6 +956,25 @@ u8 rand_u8(void)
|
||||||
return (u8)(rand_u32());
|
return (u8)(rand_u32());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse a boolean value from an environment variable.
|
||||||
|
*
|
||||||
|
* As long as the environment variable is not set to "0", "n" or "\0",
|
||||||
|
* it would return true.
|
||||||
|
*/
|
||||||
|
bool get_env_bool(const char *env_name)
|
||||||
|
{
|
||||||
|
char *env_value_str;
|
||||||
|
|
||||||
|
env_value_str = getenv(env_name);
|
||||||
|
if (!env_value_str)
|
||||||
|
return false;
|
||||||
|
if (env_value_str[0] == '0' || env_value_str[0] == 'n' ||
|
||||||
|
env_value_str[0] == 0)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void btrfs_config_init(void)
|
void btrfs_config_init(void)
|
||||||
{
|
{
|
||||||
bconf.output_format = CMD_FORMAT_TEXT;
|
bconf.output_format = CMD_FORMAT_TEXT;
|
||||||
|
|
|
@ -117,6 +117,8 @@ u64 rand_u64(void);
|
||||||
unsigned int rand_range(unsigned int upper);
|
unsigned int rand_range(unsigned int upper);
|
||||||
void init_rand_seed(u64 seed);
|
void init_rand_seed(u64 seed);
|
||||||
|
|
||||||
|
bool get_env_bool(const char *env_name);
|
||||||
|
|
||||||
char *btrfs_test_for_multiple_profiles(int fd);
|
char *btrfs_test_for_multiple_profiles(int fd);
|
||||||
int btrfs_warn_multiple_profiles(int fd);
|
int btrfs_warn_multiple_profiles(int fd);
|
||||||
void btrfs_warn_experimental(const char *str);
|
void btrfs_warn_experimental(const char *str);
|
||||||
|
|
Loading…
Reference in New Issue