summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2017-09-28 12:20:02 -0400
committerNick Mathewson <nickm@torproject.org>2017-09-28 12:20:02 -0400
commit3a073c463dbade2171a1a6ec1558bd81c0ff27f9 (patch)
tree28d329ccf721ef02e2e7973337b94e84fc88d3f2
parent14614a592e43e2a6fcddb79c3f4ff5c8df60f380 (diff)
downloadtor-3a073c463dbade2171a1a6ec1558bd81c0ff27f9.tar.gz
tor-3a073c463dbade2171a1a6ec1558bd81c0ff27f9.zip
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.
-rw-r--r--src/common/compress.c4
-rw-r--r--src/common/compress.h3
-rw-r--r--src/test/test_util.c137
3 files changed, 141 insertions, 3 deletions
diff --git a/src/common/compress.c b/src/common/compress.c
index 520148f83d..c13a3b0aed 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 ecb20668e4..23a9817479 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 49ebdf2111..162f6fbc0a 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),