Skip to content

Commit b98b208

Browse files
adam900710kdave
authored andcommitted
btrfs: reject invalid compression level
Inspired by recent changes to compression level parsing in 6db1df4 ("btrfs: accept and ignore compression level for lzo") it turns out that we do not do any extra validation for compression level input string, thus allowing things like "compress=lzo:invalid" to be accepted without warnings. Although we accept levels that are beyond the supported algorithm ranges, accepting completely invalid level specification is not correct. 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 wrong levels like "lzo:invalid". Signed-off-by: Qu Wenruo <[email protected]> Reviewed-by: David Sterba <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent ed4e6b5 commit b98b208

File tree

3 files changed

+33
-18
lines changed

3 files changed

+33
-18
lines changed

fs/btrfs/compression.c

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1616,25 +1616,29 @@ int btrfs_compress_heuristic(struct btrfs_inode *inode, u64 start, u64 end)
16161616
}
16171617

16181618
/*
1619-
* Convert the compression suffix (eg. after "zlib" starting with ":") to
1620-
* level, unrecognized string will set the default level. Negative level
1621-
* numbers are allowed.
1619+
* Convert the compression suffix (eg. after "zlib" starting with ":") to level.
1620+
*
1621+
* If the resulting level exceeds the algo's supported levels, it will be clamped.
1622+
*
1623+
* Return <0 if no valid string can be found.
1624+
* Return 0 if everything is fine.
16221625
*/
1623-
int btrfs_compress_str2level(unsigned int type, const char *str)
1626+
int btrfs_compress_str2level(unsigned int type, const char *str, int *level_ret)
16241627
{
16251628
int level = 0;
16261629
int ret;
16271630

1628-
if (!type)
1631+
if (!type) {
1632+
*level_ret = btrfs_compress_set_level(type, level);
16291633
return 0;
1634+
}
16301635

16311636
if (str[0] == ':') {
16321637
ret = kstrtoint(str + 1, 10, &level);
16331638
if (ret)
1634-
level = 0;
1639+
return ret;
16351640
}
16361641

1637-
level = btrfs_compress_set_level(type, level);
1638-
1639-
return level;
1642+
*level_ret = btrfs_compress_set_level(type, level);
1643+
return 0;
16401644
}

fs/btrfs/compression.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ void btrfs_submit_compressed_write(struct btrfs_ordered_extent *ordered,
102102
bool writeback);
103103
void btrfs_submit_compressed_read(struct btrfs_bio *bbio);
104104

105-
int btrfs_compress_str2level(unsigned int type, const char *str);
105+
int btrfs_compress_str2level(unsigned int type, const char *str, int *level_ret);
106106

107107
struct folio *btrfs_alloc_compr_folio(void);
108108
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
@@ -276,6 +276,7 @@ static int btrfs_parse_compress(struct btrfs_fs_context *ctx,
276276
const struct fs_parameter *param, int opt)
277277
{
278278
const char *string = param->string;
279+
int ret;
279280

280281
/*
281282
* Provide the same semantics as older kernels that don't use fs
@@ -294,24 +295,30 @@ static int btrfs_parse_compress(struct btrfs_fs_context *ctx,
294295
btrfs_clear_opt(ctx->mount_opt, NODATASUM);
295296
} else if (btrfs_match_compress_type(string, "zlib", true)) {
296297
ctx->compress_type = BTRFS_COMPRESS_ZLIB;
297-
ctx->compress_level = btrfs_compress_str2level(BTRFS_COMPRESS_ZLIB,
298-
string + 4);
298+
ret = btrfs_compress_str2level(BTRFS_COMPRESS_ZLIB, string + 4,
299+
&ctx->compress_level);
300+
if (ret < 0)
301+
goto error;
299302
btrfs_set_opt(ctx->mount_opt, COMPRESS);
300303
btrfs_clear_opt(ctx->mount_opt, NODATACOW);
301304
btrfs_clear_opt(ctx->mount_opt, NODATASUM);
302305
} else if (btrfs_match_compress_type(string, "lzo", true)) {
303306
ctx->compress_type = BTRFS_COMPRESS_LZO;
304-
ctx->compress_level = btrfs_compress_str2level(BTRFS_COMPRESS_LZO,
305-
string + 3);
307+
ret = btrfs_compress_str2level(BTRFS_COMPRESS_LZO, string + 3,
308+
&ctx->compress_level);
309+
if (ret < 0)
310+
goto error;
306311
if (string[3] == ':' && string[4])
307312
btrfs_warn(NULL, "Compression level ignored for LZO");
308313
btrfs_set_opt(ctx->mount_opt, COMPRESS);
309314
btrfs_clear_opt(ctx->mount_opt, NODATACOW);
310315
btrfs_clear_opt(ctx->mount_opt, NODATASUM);
311316
} else if (btrfs_match_compress_type(string, "zstd", true)) {
312317
ctx->compress_type = BTRFS_COMPRESS_ZSTD;
313-
ctx->compress_level = btrfs_compress_str2level(BTRFS_COMPRESS_ZSTD,
314-
string + 4);
318+
ret = btrfs_compress_str2level(BTRFS_COMPRESS_ZSTD, string + 4,
319+
&ctx->compress_level);
320+
if (ret < 0)
321+
goto error;
315322
btrfs_set_opt(ctx->mount_opt, COMPRESS);
316323
btrfs_clear_opt(ctx->mount_opt, NODATACOW);
317324
btrfs_clear_opt(ctx->mount_opt, NODATASUM);
@@ -322,10 +329,14 @@ static int btrfs_parse_compress(struct btrfs_fs_context *ctx,
322329
btrfs_clear_opt(ctx->mount_opt, COMPRESS);
323330
btrfs_clear_opt(ctx->mount_opt, FORCE_COMPRESS);
324331
} else {
325-
btrfs_err(NULL, "unrecognized compression value %s", string);
326-
return -EINVAL;
332+
ret = -EINVAL;
333+
goto error;
327334
}
328335
return 0;
336+
error:
337+
btrfs_err(NULL, "failed to parse compression option '%s'", string);
338+
return ret;
339+
329340
}
330341

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

0 commit comments

Comments
 (0)