summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2021-11-05 14:07:56 +1030
committerAlan Modra <amodra@gmail.com>2021-11-05 14:15:18 +1030
commitc5967f38de59c7375970c09b2c8b8702a01eb9d2 (patch)
tree21419b8ba4297ff9b92edb3a47dbedc953b278ea
parenta3c0896d80d21545bd8036deb5e1ad4027db5677 (diff)
downloadbinutils-gdb-c5967f38de59c7375970c09b2c8b8702a01eb9d2.tar.gz
PR28530, Hang in objdump on machine with 196GB RAM
Investigating the PR28530 testcase, which has a fuzzed compression header with an enormous size, I noticed that decompress_contents is broken when the size doesn't fit in strm.avail_out. It wouldn't be too hard to support larger sizes (patches welcome!) but for now just stop decompress_contents from returning rubbish. PR 28530 * compress.c (decompress_contents): Fail when uncompressed_size is too big. (bfd_init_section_decompress_status): Likewise.
-rw-r--r--bfd/compress.c15
1 files changed, 15 insertions, 0 deletions
diff --git a/bfd/compress.c b/bfd/compress.c
index 4a2ada3e3eb..70daf4ed3a5 100644
--- a/bfd/compress.c
+++ b/bfd/compress.c
@@ -46,6 +46,11 @@ decompress_contents (bfd_byte *compressed_buffer,
strm.avail_in = compressed_size;
strm.next_in = (Bytef*) compressed_buffer;
strm.avail_out = uncompressed_size;
+ /* FIXME: strm.avail_in and strm.avail_out are typically unsigned
+ int. Supporting sizes that don't fit in an unsigned int is
+ possible but will require some rewriting of this function. */
+ if (strm.avail_in != compressed_size || strm.avail_out != uncompressed_size)
+ return false;
BFD_ASSERT (Z_OK == 0);
rc = inflateInit (&strm);
@@ -516,6 +521,7 @@ bfd_init_section_decompress_status (bfd *abfd, sec_ptr sec)
int header_size;
bfd_size_type uncompressed_size;
unsigned int uncompressed_alignment_power = 0;
+ z_stream strm;
compression_header_size = bfd_get_compression_header_size (abfd, sec);
if (compression_header_size > MAX_COMPRESSION_HEADER_SIZE)
@@ -551,6 +557,15 @@ bfd_init_section_decompress_status (bfd *abfd, sec_ptr sec)
return false;
}
+ /* PR28530, reject sizes unsupported by decompress_contents. */
+ strm.avail_in = sec->size;
+ strm.avail_out = uncompressed_size;
+ if (strm.avail_in != sec->size || strm.avail_out != uncompressed_size)
+ {
+ bfd_set_error (bfd_error_nonrepresentable_section);
+ return false;
+ }
+
sec->compressed_size = sec->size;
sec->size = uncompressed_size;
bfd_set_section_alignment (sec, uncompressed_alignment_power);