mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-27 22:03:31 +01:00
Merge branch 'zlib_coverage_squashed'
This commit is contained in:
commit
81cfd5c9a1
3
changes/test_zlib_bombs
Normal file
3
changes/test_zlib_bombs
Normal file
@ -0,0 +1,3 @@
|
||||
o Testing:
|
||||
- We now have unit tests for our code to reject zlib "compression bombs".
|
||||
(Fortunately, the code works fine.)
|
4
changes/zlib_12
Normal file
4
changes/zlib_12
Normal file
@ -0,0 +1,4 @@
|
||||
o New system requirements:
|
||||
- We now require zlib version 1.2 or later. (Back when we started,
|
||||
zlib 1.1 and zlib 1.0 were still found in the wild. 1.2 was released in
|
||||
2003. We recommend the latest version.)
|
@ -46,34 +46,16 @@
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#if defined ZLIB_VERNUM && ZLIB_VERNUM < 0x1200
|
||||
#error "We require zlib version 1.2 or later."
|
||||
#endif
|
||||
|
||||
static size_t tor_zlib_state_size_precalc(int inflate,
|
||||
int windowbits, int memlevel);
|
||||
|
||||
/** Total number of bytes allocated for zlib state */
|
||||
static size_t total_zlib_allocation = 0;
|
||||
|
||||
/** Set to 1 if zlib is a version that supports gzip; set to 0 if it doesn't;
|
||||
* set to -1 if we haven't checked yet. */
|
||||
static int gzip_is_supported = -1;
|
||||
|
||||
/** Return true iff we support gzip-based compression. Otherwise, we need to
|
||||
* use zlib. */
|
||||
int
|
||||
is_gzip_supported(void)
|
||||
{
|
||||
if (gzip_is_supported >= 0)
|
||||
return gzip_is_supported;
|
||||
|
||||
if (!strcmpstart(ZLIB_VERSION, "0.") ||
|
||||
!strcmpstart(ZLIB_VERSION, "1.0") ||
|
||||
!strcmpstart(ZLIB_VERSION, "1.1"))
|
||||
gzip_is_supported = 0;
|
||||
else
|
||||
gzip_is_supported = 1;
|
||||
|
||||
return gzip_is_supported;
|
||||
}
|
||||
|
||||
/** Return a string representation of the version of the currently running
|
||||
* version of zlib. */
|
||||
const char *
|
||||
@ -165,12 +147,6 @@ tor_gzip_compress(char **out, size_t *out_len,
|
||||
|
||||
*out = NULL;
|
||||
|
||||
if (method == GZIP_METHOD && !is_gzip_supported()) {
|
||||
/* Old zlib version don't support gzip in deflateInit2 */
|
||||
log_warn(LD_BUG, "Gzip not supported with zlib %s", ZLIB_VERSION);
|
||||
goto err;
|
||||
}
|
||||
|
||||
stream = tor_malloc_zero(sizeof(struct z_stream_s));
|
||||
stream->zalloc = Z_NULL;
|
||||
stream->zfree = Z_NULL;
|
||||
@ -182,9 +158,11 @@ tor_gzip_compress(char **out, size_t *out_len,
|
||||
method_bits(method, HIGH_COMPRESSION),
|
||||
get_memlevel(HIGH_COMPRESSION),
|
||||
Z_DEFAULT_STRATEGY) != Z_OK) {
|
||||
//LCOV_EXCL_START -- we can only provoke failure by giving junk arguments.
|
||||
log_warn(LD_GENERAL, "Error from deflateInit2: %s",
|
||||
stream->msg?stream->msg:"<no message>");
|
||||
goto err;
|
||||
//LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
/* Guess 50% compression. */
|
||||
@ -237,13 +215,12 @@ tor_gzip_compress(char **out, size_t *out_len,
|
||||
* the newly unsigned field isn't negative." */
|
||||
tor_assert(stream->total_out >= 0);
|
||||
#endif
|
||||
if (((size_t)stream->total_out) > out_size + 4097) {
|
||||
/* If we're wasting more than 4k, don't. */
|
||||
*out = tor_realloc(*out, stream->total_out + 1);
|
||||
}
|
||||
if (deflateEnd(stream)!=Z_OK) {
|
||||
// LCOV_EXCL_START -- unreachable if we handled the zlib structure right
|
||||
tor_assert_nonfatal_unreached();
|
||||
log_warn(LD_BUG, "Error freeing gzip structures");
|
||||
goto err;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
tor_free(stream);
|
||||
|
||||
@ -291,12 +268,6 @@ tor_gzip_uncompress(char **out, size_t *out_len,
|
||||
tor_assert(in);
|
||||
tor_assert(in_len < UINT_MAX);
|
||||
|
||||
if (method == GZIP_METHOD && !is_gzip_supported()) {
|
||||
/* Old zlib version don't support gzip in inflateInit2 */
|
||||
log_warn(LD_BUG, "Gzip not supported with zlib %s", ZLIB_VERSION);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*out = NULL;
|
||||
|
||||
stream = tor_malloc_zero(sizeof(struct z_stream_s));
|
||||
@ -308,9 +279,11 @@ tor_gzip_uncompress(char **out, size_t *out_len,
|
||||
|
||||
if (inflateInit2(stream,
|
||||
method_bits(method, HIGH_COMPRESSION)) != Z_OK) {
|
||||
// LCOV_EXCL_START -- can only hit this if we give bad inputs.
|
||||
log_warn(LD_GENERAL, "Error from inflateInit2: %s",
|
||||
stream->msg?stream->msg:"<no message>");
|
||||
goto err;
|
||||
// LCOV_EXCL_STOP
|
||||
}
|
||||
|
||||
out_size = in_len * 2; /* guess 50% compression. */
|
||||
@ -451,12 +424,6 @@ tor_zlib_new(int compress, compress_method_t method,
|
||||
tor_zlib_state_t *out;
|
||||
int bits, memlevel;
|
||||
|
||||
if (method == GZIP_METHOD && !is_gzip_supported()) {
|
||||
/* Old zlib version don't support gzip in inflateInit2 */
|
||||
log_warn(LD_BUG, "Gzip not supported with zlib %s", ZLIB_VERSION);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (! compress) {
|
||||
/* use this setting for decompression, since we might have the
|
||||
* max number of window bits */
|
||||
@ -474,10 +441,10 @@ tor_zlib_new(int compress, compress_method_t method,
|
||||
if (deflateInit2(&out->stream, Z_BEST_COMPRESSION, Z_DEFLATED,
|
||||
bits, memlevel,
|
||||
Z_DEFAULT_STRATEGY) != Z_OK)
|
||||
goto err;
|
||||
goto err; // LCOV_EXCL_LINE
|
||||
} else {
|
||||
if (inflateInit2(&out->stream, bits) != Z_OK)
|
||||
goto err;
|
||||
goto err; // LCOV_EXCL_LINE
|
||||
}
|
||||
out->allocation = tor_zlib_state_size_precalc(!compress, bits, memlevel);
|
||||
|
||||
|
@ -1821,22 +1821,21 @@ test_util_gzip(void *arg)
|
||||
(void)arg;
|
||||
buf1 = tor_strdup("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAAAAAAAAAAAAAAAAZ");
|
||||
tt_assert(detect_compression_method(buf1, strlen(buf1)) == UNKNOWN_METHOD);
|
||||
if (is_gzip_supported()) {
|
||||
tt_assert(!tor_gzip_compress(&buf2, &len1, buf1, strlen(buf1)+1,
|
||||
GZIP_METHOD));
|
||||
tt_assert(buf2);
|
||||
tt_assert(len1 < strlen(buf1));
|
||||
tt_assert(detect_compression_method(buf2, len1) == GZIP_METHOD);
|
||||
|
||||
tt_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1,
|
||||
GZIP_METHOD, 1, LOG_INFO));
|
||||
tt_assert(buf3);
|
||||
tt_int_op(strlen(buf1) + 1,OP_EQ, len2);
|
||||
tt_str_op(buf1,OP_EQ, buf3);
|
||||
tt_assert(!tor_gzip_compress(&buf2, &len1, buf1, strlen(buf1)+1,
|
||||
GZIP_METHOD));
|
||||
tt_assert(buf2);
|
||||
tt_assert(len1 < strlen(buf1));
|
||||
tt_assert(detect_compression_method(buf2, len1) == GZIP_METHOD);
|
||||
|
||||
tor_free(buf2);
|
||||
tor_free(buf3);
|
||||
}
|
||||
tt_assert(!tor_gzip_uncompress(&buf3, &len2, buf2, len1,
|
||||
GZIP_METHOD, 1, LOG_INFO));
|
||||
tt_assert(buf3);
|
||||
tt_int_op(strlen(buf1) + 1,OP_EQ, len2);
|
||||
tt_str_op(buf1,OP_EQ, buf3);
|
||||
|
||||
tor_free(buf2);
|
||||
tor_free(buf3);
|
||||
|
||||
tt_assert(!tor_gzip_compress(&buf2, &len1, buf1, strlen(buf1)+1,
|
||||
ZLIB_METHOD));
|
||||
@ -1920,6 +1919,53 @@ test_util_gzip(void *arg)
|
||||
tor_free(buf1);
|
||||
}
|
||||
|
||||
static void
|
||||
test_util_gzip_compression_bomb(void *arg)
|
||||
{
|
||||
/* A 'compression bomb' is a very small object that uncompresses to a huge
|
||||
* one. Most compression formats support them, but they can be a DOS vector.
|
||||
* In Tor we try not to generate them, and we don't accept them.
|
||||
*/
|
||||
(void) arg;
|
||||
size_t one_million = 1<<20;
|
||||
char *one_mb = tor_malloc_zero(one_million);
|
||||
char *result = NULL;
|
||||
size_t result_len = 0;
|
||||
tor_zlib_state_t *state = NULL;
|
||||
|
||||
/* Make sure we can't produce a compression bomb */
|
||||
tt_int_op(-1, OP_EQ, tor_gzip_compress(&result, &result_len,
|
||||
one_mb, one_million,
|
||||
ZLIB_METHOD));
|
||||
|
||||
/* Here's a compression bomb that we made manually. */
|
||||
const char compression_bomb[1039] =
|
||||
{ 0x78, 0xDA, 0xED, 0xC1, 0x31, 0x01, 0x00, 0x00, 0x00, 0xC2,
|
||||
0xA0, 0xF5, 0x4F, 0x6D, 0x08, 0x5F, 0xA0 /* .... */ };
|
||||
tt_int_op(-1, OP_EQ, tor_gzip_uncompress(&result, &result_len,
|
||||
compression_bomb, 1039,
|
||||
ZLIB_METHOD, 0, LOG_WARN));
|
||||
|
||||
|
||||
/* Now try streaming that. */
|
||||
state = tor_zlib_new(0, ZLIB_METHOD, HIGH_COMPRESSION);
|
||||
tor_zlib_output_t r;
|
||||
const char *inp = compression_bomb;
|
||||
size_t inlen = 1039;
|
||||
do {
|
||||
char *outp = one_mb;
|
||||
size_t outleft = 4096; /* small on purpose */
|
||||
r = tor_zlib_process(state, &outp, &outleft, &inp, &inlen, 0);
|
||||
tt_int_op(inlen, OP_NE, 0);
|
||||
} while (r == TOR_ZLIB_BUF_FULL);
|
||||
|
||||
tt_int_op(r, OP_EQ, TOR_ZLIB_ERR);
|
||||
|
||||
done:
|
||||
tor_free(one_mb);
|
||||
tor_zlib_free(state);
|
||||
}
|
||||
|
||||
/** Run unit tests for mmap() wrapper functionality. */
|
||||
static void
|
||||
test_util_mmap(void *arg)
|
||||
@ -4935,6 +4981,7 @@ struct testcase_t util_tests[] = {
|
||||
UTIL_LEGACY(strmisc),
|
||||
UTIL_LEGACY(pow2),
|
||||
UTIL_LEGACY(gzip),
|
||||
UTIL_LEGACY(gzip_compression_bomb),
|
||||
UTIL_LEGACY(datadir),
|
||||
UTIL_LEGACY(memarea),
|
||||
UTIL_LEGACY(control_formats),
|
||||
|
Loading…
Reference in New Issue
Block a user