Skip to content

Commit e6de747

Browse files
committed
btrfs: reject invalid compression level
Inspired by recent changes to compression level parsing by Calvin Owens, it turns out that we do not do any extra validation for compression level, thus allowing things like "compress=lzo:invalid" to be accepted without extra warning or whatever. Although we accept levels that are beyond the supported algorithm ranges, accepting completely invalid levels are beyond sanity. Fix the too loose checks for compression level, by doing proper error handling of kstrtoint(), so that we will reject not only too large values (beyond int range) but also completely insane levels like "lzo:invalid". Reviewed-by: David Sterba <[email protected]> Signed-off-by: Qu Wenruo <[email protected]>
1 parent 34d40ee commit e6de747

File tree

3 files changed

+33
-17
lines changed

3 files changed

+33
-17
lines changed

fs/btrfs/compression.c

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1652,24 +1652,29 @@ int btrfs_compress_heuristic(struct btrfs_inode *inode, u64 start, u64 end)
16521652

16531653
/*
16541654
* Convert the compression suffix (eg. after "zlib" starting with ":") to
1655-
* level, unrecognized string will set the default level. Negative level
1656-
* numbers are allowed.
1655+
* level.
1656+
*
1657+
* If the resulted level exceed the algo's supported levels, it will be clamped.
1658+
*
1659+
* Return <0 if no valid string can be found.
1660+
* Return 0 if everything is fine.
16571661
*/
1658-
int btrfs_compress_str2level(unsigned int type, const char *str)
1662+
int btrfs_compress_str2level(unsigned int type, const char *str, int *level_ret)
16591663
{
16601664
int level = 0;
16611665
int ret;
16621666

1663-
if (!type)
1667+
if (!type) {
1668+
*level_ret = btrfs_compress_set_level(type, level);
16641669
return 0;
1670+
}
16651671

16661672
if (str[0] == ':') {
16671673
ret = kstrtoint(str + 1, 10, &level);
16681674
if (ret)
1669-
level = 0;
1675+
return ret;
16701676
}
16711677

1672-
level = btrfs_compress_set_level(type, level);
1673-
1674-
return level;
1678+
*level_ret = btrfs_compress_set_level(type, level);
1679+
return 0;
16751680
}

fs/btrfs/compression.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ void btrfs_submit_compressed_write(struct btrfs_ordered_extent *ordered,
110110
bool writeback);
111111
void btrfs_submit_compressed_read(struct btrfs_bio *bbio);
112112

113-
int btrfs_compress_str2level(unsigned int type, const char *str);
113+
int btrfs_compress_str2level(unsigned int type, const char *str, int *level_ret);
114114

115115
struct folio *btrfs_alloc_compr_folio(void);
116116
void btrfs_free_compr_folio(struct folio *folio);

fs/btrfs/super.c

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ static int btrfs_parse_compress(struct btrfs_fs_context *ctx,
274274
const struct fs_parameter *param, int opt)
275275
{
276276
const char *string = param->string;
277+
int ret;
277278

278279
/*
279280
* Provide the same semantics as older kernels that don't use fs
@@ -292,24 +293,30 @@ static int btrfs_parse_compress(struct btrfs_fs_context *ctx,
292293
btrfs_clear_opt(ctx->mount_opt, NODATASUM);
293294
} else if (btrfs_match_compress_type(string, "zlib", true)) {
294295
ctx->compress_type = BTRFS_COMPRESS_ZLIB;
295-
ctx->compress_level = btrfs_compress_str2level(BTRFS_COMPRESS_ZLIB,
296-
string + 4);
296+
ret = btrfs_compress_str2level(BTRFS_COMPRESS_ZLIB, string + 4,
297+
&ctx->compress_level);
298+
if (ret < 0)
299+
goto error;
297300
btrfs_set_opt(ctx->mount_opt, COMPRESS);
298301
btrfs_clear_opt(ctx->mount_opt, NODATACOW);
299302
btrfs_clear_opt(ctx->mount_opt, NODATASUM);
300303
} else if (btrfs_match_compress_type(string, "lzo", true)) {
301304
ctx->compress_type = BTRFS_COMPRESS_LZO;
302-
ctx->compress_level = btrfs_compress_str2level(BTRFS_COMPRESS_LZO,
303-
string + 3);
305+
ret = btrfs_compress_str2level(BTRFS_COMPRESS_LZO, string + 3,
306+
&ctx->compress_level);
307+
if (ret < 0)
308+
goto error;
304309
if (string[3] == ':' && string[4])
305310
btrfs_warn(NULL, "Compression level ignored for LZO");
306311
btrfs_set_opt(ctx->mount_opt, COMPRESS);
307312
btrfs_clear_opt(ctx->mount_opt, NODATACOW);
308313
btrfs_clear_opt(ctx->mount_opt, NODATASUM);
309314
} else if (btrfs_match_compress_type(string, "zstd", true)) {
310315
ctx->compress_type = BTRFS_COMPRESS_ZSTD;
311-
ctx->compress_level = btrfs_compress_str2level(BTRFS_COMPRESS_ZSTD,
312-
string + 4);
316+
ret = btrfs_compress_str2level(BTRFS_COMPRESS_ZSTD, string + 4,
317+
&ctx->compress_level);
318+
if (ret < 0)
319+
goto error;
313320
btrfs_set_opt(ctx->mount_opt, COMPRESS);
314321
btrfs_clear_opt(ctx->mount_opt, NODATACOW);
315322
btrfs_clear_opt(ctx->mount_opt, NODATASUM);
@@ -320,10 +327,14 @@ static int btrfs_parse_compress(struct btrfs_fs_context *ctx,
320327
btrfs_clear_opt(ctx->mount_opt, COMPRESS);
321328
btrfs_clear_opt(ctx->mount_opt, FORCE_COMPRESS);
322329
} else {
323-
btrfs_err(NULL, "unrecognized compression value %s", string);
324-
return -EINVAL;
330+
ret = -EINVAL;
331+
goto error;
325332
}
326333
return 0;
334+
error:
335+
btrfs_err(NULL, "failed to parse compression option '%s'", string);
336+
return ret;
337+
327338
}
328339

329340
static int btrfs_parse_param(struct fs_context *fc, struct fs_parameter *param)

0 commit comments

Comments
 (0)