Skip to content

Commit d8f1bd5

Browse files
adam900710kdave
authored andcommitted
btrfs-progs: mkfs: fix a stack over-flow when features string are too long
[BUG] Even with chunk_objectid bug fixed, mkfs.btrfs can still caused stack overflow when enabling extent-tree-v2 feature (need experimental features enabled): # ./mkfs.btrfs -f -O extent-tree-v2 ~/test.img btrfs-progs v5.19.1 See http://btrfs.wiki.kernel.org for more information. ERROR: superblock magic doesn't match NOTE: several default settings have changed in version 5.15, please make sure this does not affect your deployments: - DUP for metadata (-m dup) - enabled no-holes (-O no-holes) - enabled free-space-tree (-R free-space-tree) Label: (null) UUID: 205c61e7-f58e-4e8f-9dc2-38724f5c554b Node size: 16384 Sector size: 4096 Filesystem size: 512.00MiB Block group profiles: Data: single 8.00MiB Metadata: DUP 32.00MiB System: DUP 8.00MiB SSD detected: no Zoned device: no ================================================================= [... Skip full ASAN output ...] ==65655==ABORTING [CAUSE] For experimental build, we have unified feature output, but the old buffer size is only 64 bytes, which is too small to cover the new full feature string: extref, skinny-metadata, no-holes, free-space-tree, block-group-tree, extent-tree-v2 Above feature string is already 84 bytes, over the 64 on-stack memory size. This can also be proved by the ASAN output: ==65655==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffc4e03b1d0 at pc 0x7ff0fc05fafe bp 0x7ffc4e03ac60 sp 0x7ffc4e03a408 WRITE of size 17 at 0x7ffc4e03b1d0 thread T0 #0 0x7ff0fc05fafd in __interceptor_strcat /usr/src/debug/gcc/libsanitizer/asan/asan_interceptors.cpp:377 #1 0x55cdb7b06ca5 in parse_features_to_string common/fsfeatures.c:316 #2 0x55cdb7b06ce1 in btrfs_parse_fs_features_to_string common/fsfeatures.c:324 #3 0x55cdb7a37226 in main mkfs/main.c:1783 #4 0x7ff0fbe3c28f (/usr/lib/libc.so.6+0x2328f) #5 0x7ff0fbe3c349 in __libc_start_main (/usr/lib/libc.so.6+0x23349) #6 0x55cdb7a2cb34 in _start ../sysdeps/x86_64/start.S:115 [FIX] Introduce a new macro, BTRFS_FEATURE_STRING_BUF_SIZE, along with a new sanity check helper, btrfs_assert_feature_buf_size(). The problem is I can not find a build time method to verify BTRFS_FEATURE_STRING_BUF_SIZE is large enough to contain all feature names, thus have to go the runtime function to do the BUG_ON() to verify the macro size. Now the minimal buffer size for experimental build is 138 bytes, just bump it to 160 for future expansion. And if further features go beyond that number, mkfs.btrfs/btrfs-convert will immediately crash at that BUG_ON(), so we can definitely detect it. Reviewed-by: Anand Jain <anand.jain@oracle.com> Tested-by: Anand Jain <anand.jain@oracle.com> Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 56e75c9 commit d8f1bd5

File tree

4 files changed

+37
-2
lines changed

4 files changed

+37
-2
lines changed

common/fsfeatures.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,32 @@ static const struct btrfs_feature runtime_features[] = {
251251
}
252252
};
253253

254+
/*
255+
* This is a sanity check to make sure BTRFS_FEATURE_STRING_BUF_SIZE is large
256+
* enough to contain all strings.
257+
*
258+
* All callers using btrfs_parse_*_features_to_string() should call this first.
259+
*/
260+
void btrfs_assert_feature_buf_size(void)
261+
{
262+
int total_size = 0;
263+
int i;
264+
265+
/*
266+
* This is a little over-calculated, as we include ", list-all".
267+
* But 10 extra bytes should not be a big deal.
268+
*/
269+
for (i = 0; i < ARRAY_SIZE(mkfs_features); i++)
270+
/* The extra 2 bytes are for the ", " prefix. */
271+
total_size += strlen(mkfs_features[i].name) + 2;
272+
BUG_ON(BTRFS_FEATURE_STRING_BUF_SIZE < total_size);
273+
274+
total_size = 0;
275+
for (i = 0; i < ARRAY_SIZE(runtime_features); i++)
276+
total_size += strlen(runtime_features[i].name) + 2;
277+
BUG_ON(BTRFS_FEATURE_STRING_BUF_SIZE < total_size);
278+
}
279+
254280
static size_t get_feature_array_size(enum feature_source source)
255281
{
256282
if (source == FS_FEATURES)

common/fsfeatures.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ struct btrfs_mkfs_features {
3737
#define BTRFS_FEATURE_RUNTIME_QUOTA (1ULL << 0)
3838
#define BTRFS_FEATURE_RUNTIME_LIST_ALL (1ULL << 1)
3939

40+
/*
41+
* Such buffer size should be able to contain all feature string, with extra
42+
* ", " for each feature.
43+
*/
44+
#define BTRFS_FEATURE_STRING_BUF_SIZE (160)
45+
4046
static const struct btrfs_mkfs_features btrfs_mkfs_default_features = {
4147
.compat_ro_flags = BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE |
4248
BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID,
@@ -86,5 +92,6 @@ int btrfs_check_sectorsize(u32 sectorsize);
8692
int btrfs_check_features(const struct btrfs_mkfs_features *features,
8793
const struct btrfs_mkfs_features *allowed);
8894
int btrfs_tree_search2_ioctl_supported(int fd);
95+
void btrfs_assert_feature_buf_size(void);
8996

9097
#endif

convert/main.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1147,7 +1147,7 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize,
11471147
struct btrfs_key key;
11481148
char subvol_name[SOURCE_FS_NAME_LEN + 8];
11491149
struct task_ctx ctx;
1150-
char features_buf[64];
1150+
char features_buf[BTRFS_FEATURE_STRING_BUF_SIZE];
11511151
char fsid_str[BTRFS_UUID_UNPARSED_SIZE];
11521152
struct btrfs_mkfs_config mkfs_cfg;
11531153
bool btrfs_sb_committed = false;
@@ -1835,6 +1835,7 @@ int BOX_MAIN(convert)(int argc, char *argv[])
18351835
char fsid[BTRFS_UUID_UNPARSED_SIZE] = {0};
18361836

18371837
crc32c_optimization_init();
1838+
btrfs_assert_feature_buf_size();
18381839
printf("btrfs-convert from %s\n\n", PACKAGE_STRING);
18391840

18401841
while(1) {

mkfs/main.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
10281028

10291029
crc32c_optimization_init();
10301030
btrfs_config_init();
1031+
btrfs_assert_feature_buf_size();
10311032

10321033
while(1) {
10331034
int c;
@@ -1750,7 +1751,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
17501751
}
17511752
}
17521753
if (bconf.verbose) {
1753-
char features_buf[64];
1754+
char features_buf[BTRFS_FEATURE_STRING_BUF_SIZE];
17541755

17551756
update_chunk_allocation(fs_info, &allocation);
17561757
printf("Label: %s\n", label);

0 commit comments

Comments
 (0)