Commit 3a073c46 authored by Nick Mathewson's avatar Nick Mathewson
Browse files

Improve unit test coverage for compression code.

These tests try uncompressing garbage, verify that we won't
make compression bombs, and verify that we won't uncompress
compression bombs.
parent 14614a59
Pipeline #12254205 failed with stage
in 10 minutes and 5 seconds
......@@ -51,8 +51,8 @@ static atomic_counter_t total_compress_allocation;
/** Return true if uncompressing an input of size <b>in_size</b> to an input of
* size at least <b>size_out</b> looks like a compression bomb. */
int
tor_compress_is_compression_bomb(size_t size_in, size_t size_out)
MOCK_IMPL(int,
tor_compress_is_compression_bomb,(size_t size_in, size_t size_out))
{
if (size_in == 0 || size_out < CHECK_FOR_COMPRESSION_BOMB_AFTER)
return 0;
......
......@@ -45,7 +45,8 @@ int tor_uncompress(char **out, size_t *out_len,
compress_method_t detect_compression_method(const char *in, size_t in_len);
int tor_compress_is_compression_bomb(size_t size_in, size_t size_out);
MOCK_DECL(int,tor_compress_is_compression_bomb,(size_t size_in,
size_t size_out));
int tor_compress_supports_method(compress_method_t method);
unsigned tor_compress_get_supported_method_bitmask(void);
......
......@@ -2480,6 +2480,125 @@ test_util_decompress_concatenated(void *arg)
;
}
static void
test_util_decompress_junk_impl(compress_method_t method)
{
char input[4096];
char *result = NULL, *result2 = NULL;
size_t szr, szr2, sz;
int r;
/* This shouldn't be a compressed string according to any method. */
strlcpy(input, "This shouldn't be a compressed string by any means.",
sizeof(input));
sz = strlen(input);
setup_capture_of_logs(LOG_WARN);
r = tor_uncompress(&result, &szr, input, sz, method, 0, LOG_WARN);
tt_int_op(r, OP_EQ, -1);
tt_ptr_op(result, OP_EQ, NULL);
expect_log_msg_containing("Error while uncompressing data: bad input?");
mock_clean_saved_logs();
/* Now try again, with a compressed object that starts out good and turns to
junk. */
crypto_rand(input, sizeof(input));
r = tor_compress(&result, &szr, input, sizeof(input), method);
tt_int_op(r, OP_EQ, 0);
crypto_rand(result+szr/2, szr-(szr/2)); // trash the 2nd half of the result
r = tor_uncompress(&result2, &szr2, result, szr, method, 0, LOG_WARN);
tt_int_op(r, OP_EQ, -1);
expect_log_msg_containing("Error while uncompressing data: bad input?");
done:
teardown_capture_of_logs();
tor_free(result);
tor_free(result2);
}
static void
test_util_decompress_junk(void *arg)
{
const char *methodname = arg;
tt_assert(methodname);
compress_method_t method = compression_method_get_by_name(methodname);
tt_int_op(method, OP_NE, UNKNOWN_METHOD);
if (! tor_compress_supports_method(method)) {
tt_skip();
}
test_util_decompress_junk_impl(method);
done:
;
}
/* mock replacement for tor_compress_is_compression_bomb that doesn't
* believe in compression bombs. */
static int
mock_is_never_compression_bomb(size_t in, size_t out)
{
(void)in;
(void) out;
return 0;
}
static void
test_util_decompress_dos_impl(compress_method_t method)
{
char *input;
char *result = NULL, *result2 = NULL;
size_t szr, szr2;
int r;
const size_t big = 1024*1024;
/* one megabyte of 0s. */
input = tor_malloc_zero(big);
/* Compress it into "result": it should fail. */
setup_full_capture_of_logs(LOG_WARN);
r = tor_compress(&result, &szr, input, big, method);
tt_int_op(r, OP_EQ, -1);
expect_log_msg_containing(
"other Tors would think this was a compression bomb");
teardown_capture_of_logs();
/* Try again, but this time suppress compression-bomb detection */
MOCK(tor_compress_is_compression_bomb, mock_is_never_compression_bomb);
r = tor_compress(&result, &szr, input, big, method);
UNMOCK(tor_compress_is_compression_bomb);
tt_int_op(r, OP_EQ, 0);
tt_ptr_op(result, OP_NE, NULL);
/* We should refuse to uncomrpess it again, since it looks like a
* compression bomb. */
setup_capture_of_logs(LOG_WARN);
r = tor_uncompress(&result2, &szr2, result, szr, method, 0, LOG_WARN);
tt_int_op(r, OP_EQ, -1);
expect_log_msg_containing("bomb; abandoning stream");
done:
teardown_capture_of_logs();
tor_free(result);
tor_free(result2);
}
static void
test_util_decompress_dos(void *arg)
{
const char *methodname = arg;
tt_assert(methodname);
compress_method_t method = compression_method_get_by_name(methodname);
tt_int_op(method, OP_NE, UNKNOWN_METHOD);
if (! tor_compress_supports_method(method)) {
tt_skip();
}
test_util_decompress_dos_impl(method);
done:
;
}
static void
test_util_gzip_compression_bomb(void *arg)
{
......@@ -5917,6 +6036,16 @@ test_util_get_unquoted_path(void *arg)
&passthrough_setup, \
(char*)(identifier) }
#define COMPRESS_JUNK(name, identifier) \
{ "compress_junk/" #name, test_util_decompress_junk, 0, \
&passthrough_setup, \
(char*)(identifier) }
#define COMPRESS_DOS(name, identifier) \
{ "compress_dos/" #name, test_util_decompress_dos, 0, \
&passthrough_setup, \
(char*)(identifier) }
#ifdef _WIN32
#define UTIL_TEST_NO_WIN(n, f) { #n, NULL, TT_SKIP, NULL, NULL }
#define UTIL_TEST_WIN_ONLY(n, f) UTIL_TEST(n, (f))
......@@ -5951,6 +6080,14 @@ struct testcase_t util_tests[] = {
COMPRESS_CONCAT(lzma, "x-tor-lzma"),
COMPRESS_CONCAT(zstd, "x-zstd"),
COMPRESS_CONCAT(none, "identity"),
COMPRESS_JUNK(zlib, "deflate"),
COMPRESS_JUNK(gzip, "gzip"),
COMPRESS_JUNK(lzma, "x-tor-lzma"),
COMPRESS_DOS(zlib, "deflate"),
COMPRESS_DOS(gzip, "gzip"),
COMPRESS_DOS(lzma, "x-tor-lzma"),
// Disabled for now, since it triggers #23551
// COMPRESS_DOS(zstd, "x-zstd"),
UTIL_TEST(gzip_compression_bomb, TT_FORK),
UTIL_LEGACY(datadir),
UTIL_LEGACY(memarea),
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment