commit 3a073c463dbade2171a1a6ec1558bd81c0ff27f9 Author: Nick Mathewson nickm@torproject.org Date: Thu Sep 28 12:20:02 2017 -0400
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. --- src/common/compress.c | 4 +- src/common/compress.h | 3 +- src/test/test_util.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+), 3 deletions(-)
diff --git a/src/common/compress.c b/src/common/compress.c index 520148f83..c13a3b0ae 100644 --- a/src/common/compress.c +++ b/src/common/compress.c @@ -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; diff --git a/src/common/compress.h b/src/common/compress.h index ecb20668e..23a981747 100644 --- a/src/common/compress.h +++ b/src/common/compress.h @@ -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); diff --git a/src/test/test_util.c b/src/test/test_util.c index 49ebdf211..162f6fbc0 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -2481,6 +2481,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) { /* A 'compression bomb' is a very small object that uncompresses to a huge @@ -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),
tor-commits@lists.torproject.org