From 572e4c5db077401885d4966120db47f7ae762d04 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Fri, 14 Jan 2022 21:35:32 -0800 Subject: [PATCH] gunzip: limit uncompressed buffer size to 1032x compressed size Don't allocate an obviously-too-large buffer for uncompressed data if ISIZE becomes corrupted in a small file. This isn't too effective, as we still must allow over 1000x expansion, but we might as well do this. Update https://github.com/ebiggers/libdeflate/issues/157 --- programs/gzip.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/programs/gzip.c b/programs/gzip.c index a1d6d2a..2193cf2 100644 --- a/programs/gzip.c +++ b/programs/gzip.c @@ -192,6 +192,7 @@ do_decompress(struct libdeflate_decompressor *decompressor, size_t compressed_size = in->mmap_size; void *uncompressed_data = NULL; size_t uncompressed_size; + size_t max_uncompressed_size; size_t actual_in_nbytes; size_t actual_out_nbytes; enum libdeflate_result result; @@ -214,8 +215,23 @@ do_decompress(struct libdeflate_decompressor *decompressor, if (uncompressed_size == 0) uncompressed_size = 1; + /* + * DEFLATE cannot expand data more than 1032x, so there's no need to + * ever allocate a buffer more than 1032 times larger than the + * compressed data. This is a fail-safe, albeit not a very good one, if + * ISIZE becomes corrupted on a small file. (The 1032x number comes + * from each 2 bits generating a 258-byte match. This is a hard upper + * bound; the real upper bound is slightly smaller due to overhead.) + */ + if (compressed_size <= SIZE_MAX / 1032) + max_uncompressed_size = compressed_size * 1032; + else + max_uncompressed_size = SIZE_MAX; + do { if (uncompressed_data == NULL) { + uncompressed_size = MIN(uncompressed_size, + max_uncompressed_size); uncompressed_data = xmalloc(uncompressed_size); if (uncompressed_data == NULL) { msg("%"TS": file is probably too large to be " @@ -234,6 +250,11 @@ do_decompress(struct libdeflate_decompressor *decompressor, &actual_out_nbytes); if (result == LIBDEFLATE_INSUFFICIENT_SPACE) { + if (uncompressed_size >= max_uncompressed_size) { + msg("Bug in libdeflate_gzip_decompress_ex(): data expanded too much!"); + ret = -1; + goto out; + } if (uncompressed_size * 2 <= uncompressed_size) { msg("%"TS": file corrupt or too large to be " "processed by this program", in->name); @@ -256,7 +277,7 @@ do_decompress(struct libdeflate_decompressor *decompressor, if (actual_in_nbytes == 0 || actual_in_nbytes > compressed_size || actual_out_nbytes > uncompressed_size) { - msg("Bug in libdeflate_gzip_decompress_ex()!"); + msg("Bug in libdeflate_gzip_decompress_ex(): impossible actual_nbytes value!"); ret = -1; goto out; }