[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[or-cvs] [tor/release-0.2.2] Merge remote branch 'origin/maint-0.2.1' into maint-0.2.2
commit cff4cfef4ff8813fbc47a9f914bb09bc011cb101
Merge: ed87738 8f11642
Author: Nick Mathewson <nickm@xxxxxxxxxxxxxx>
Date: Sat Jan 15 12:13:50 2011 -0500
Merge remote branch 'origin/maint-0.2.1' into maint-0.2.2
changes/bug2324_uncompress | 5 +++
src/common/torgzip.c | 60 ++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 63 insertions(+), 2 deletions(-)
diff --combined src/common/torgzip.c
index 8c4dca9,249151c..b2a205f
--- a/src/common/torgzip.c
+++ b/src/common/torgzip.c
@@@ -13,42 -13,20 +13,42 @@@
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
-#ifdef _MSC_VER
-#include "..\..\contrib\zlib\zlib.h"
-#else
-#include <zlib.h>
-#endif
#include <string.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
+#include "torint.h"
#include "util.h"
-#include "log.h"
+#include "torlog.h"
#include "torgzip.h"
+/* zlib 1.2.4 and 1.2.5 do some "clever" things with macros. Instead of
+ saying "(defined(FOO) ? FOO : 0)" they like to say "FOO-0", on the theory
+ that nobody will care if the compile outputs a no-such-identifier warning.
+
+ Sorry, but we like -Werror over here, so I guess we need to define these.
+ I hope that zlib 1.2.6 doesn't break these too.
+*/
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE 0
+#endif
+#ifndef _LFS64_LARGEFILE
+#define _LFS64_LARGEFILE 0
+#endif
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 0
+#endif
+#ifndef off64_t
+#define off64_t int64_t
+#endif
+
+#ifdef _MSC_VER
+#include "..\..\contrib\zlib\zlib.h"
+#else
+#include <zlib.h>
+#endif
+
/** 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;
@@@ -79,6 -57,33 +79,33 @@@ method_bits(compress_method_t method
return method == GZIP_METHOD ? 15+16 : 15;
}
+ /* These macros define the maximum allowable compression factor. Anything of
+ * size greater than CHECK_FOR_COMPRESSION_BOMB_AFTER is not allowed to
+ * have an uncompression factor (uncompressed size:compressed size ratio) of
+ * any greater than MAX_UNCOMPRESSION_FACTOR.
+ *
+ * Picking a value for MAX_UNCOMPRESSION_FACTOR is a trade-off: we want it to
+ * be small to limit the attack multiplier, but we also want it to be large
+ * enough so that no legitimate document --even ones we might invent in the
+ * future -- ever compresses by a factor of greater than
+ * MAX_UNCOMPRESSION_FACTOR. Within those parameters, there's a reasonably
+ * large range of possible values. IMO, anything over 8 is probably safe; IMO
+ * anything under 50 is probably sufficient.
+ */
+ #define MAX_UNCOMPRESSION_FACTOR 25
+ #define CHECK_FOR_COMPRESSION_BOMB_AFTER (1024*64)
+
+ /** 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. */
+ static int
+ is_compression_bomb(size_t size_in, size_t size_out)
+ {
+ if (size_in == 0 || size_out < CHECK_FOR_COMPRESSION_BOMB_AFTER)
+ return 0;
+
+ return (size_out / size_in > MAX_UNCOMPRESSION_FACTOR);
+ }
+
/** Given <b>in_len</b> bytes at <b>in</b>, compress them into a newly
* allocated buffer, using the method described in <b>method</b>. Store the
* compressed string in *<b>out</b>, and its length in *<b>out_len</b>.
@@@ -181,13 -186,21 +208,19 @@@ tor_gzip_compress(char **out, size_t *o
}
tor_free(stream);
+ if (is_compression_bomb(*out_len, in_len)) {
+ log_warn(LD_BUG, "We compressed something and got an insanely high "
+ "compression factor; other Tors would think this was a zlib bomb.");
+ goto err;
+ }
+
return 0;
err:
if (stream) {
deflateEnd(stream);
tor_free(stream);
}
- if (*out) {
- tor_free(*out);
- }
+ tor_free(*out);
return -1;
}
@@@ -243,7 -256,7 +276,7 @@@ tor_gzip_uncompress(char **out, size_t
out_size = in_len * 2; /* guess 50% compression. */
if (out_size < 1024) out_size = 1024;
- if (out_size > UINT_MAX)
+ if (out_size >= SIZE_T_CEILING || out_size > UINT_MAX)
goto err;
*out = tor_malloc(out_size);
@@@ -283,7 -296,16 +316,16 @@@
old_size = out_size;
out_size *= 2;
if (out_size < old_size) {
- log_warn(LD_GENERAL, "Size overflow in compression.");
+ log_warn(LD_GENERAL, "Size overflow in uncompression.");
+ goto err;
+ }
+ if (is_compression_bomb(in_len, out_size)) {
+ log_warn(LD_GENERAL, "Input looks like a possible zlib bomb; "
+ "not proceeding.");
+ goto err;
+ }
+ if (out_size >= SIZE_T_CEILING) {
+ log_warn(LD_BUG, "Hit SIZE_T_CEILING limit while uncompressing.");
goto err;
}
*out = tor_realloc(*out, out_size);
@@@ -349,6 -371,11 +391,11 @@@ detect_compression_method(const char *i
struct tor_zlib_state_t {
struct z_stream_s stream;
int compress;
+
+ /* Number of bytes read so far. Used to detect zlib bombs. */
+ size_t input_so_far;
+ /* Number of bytes written so far. Used to detect zlib bombs. */
+ size_t output_so_far;
};
/** Construct and return a tor_zlib_state_t object using <b>method</b>. If
@@@ -415,11 -442,20 +462,20 @@@ tor_zlib_process(tor_zlib_state_t *stat
err = inflate(&state->stream, finish ? Z_FINISH : Z_SYNC_FLUSH);
}
+ state->input_so_far += state->stream.next_in - ((unsigned char*)*in);
+ state->output_so_far += state->stream.next_out - ((unsigned char*)*out);
+
*out = (char*) state->stream.next_out;
*out_len = state->stream.avail_out;
*in = (const char *) state->stream.next_in;
*in_len = state->stream.avail_in;
+ if (! state->compress &&
+ is_compression_bomb(state->input_so_far, state->output_so_far)) {
+ log_warn(LD_DIR, "Possible zlib bomb; abandoning stream.");
+ return TOR_ZLIB_ERR;
+ }
+
switch (err)
{
case Z_STREAM_END:
@@@ -443,8 -479,7 +499,8 @@@
void
tor_zlib_free(tor_zlib_state_t *state)
{
- tor_assert(state);
+ if (!state)
+ return;
if (state->compress)
deflateEnd(&state->stream);