__libelf_decompress_zstd () allocates the output buffer from the
uncompressed size taken out of the section's compression header:
void *buf_out = malloc (size_out ?: 1);
size_out is chdr.ch_size, an attacker-controlled field of the section's
Elf{32,64}_Chdr. The zlib decompressor rejects implausible expansion
ratios before allocating:
if (unlikely (size_out / 1032 > size_in))
{
__libelf_seterrno (ELF_E_INVALID_DATA);
return NULL;
}
but __libelf_decompress_zstd () has no such guard, so a section whose
header declares a huge ch_size over a tiny compressed payload drives an
arbitrarily large allocation. The path is reachable from
__libelf_decompress_elf (), i.e. via elf_compress (scn, 0, 0) and via
elf_strptr () on a SHF_COMPRESSED string section, so any consumer that
reads a crafted ELF can be made to request the allocation.
Add the same ratio check to __libelf_decompress_zstd () before the
malloc, using a conservative 32768:1 upper bound.
Reproduced with a 305-byte ELF whose .comp section is SHF_COMPRESSED
with ch_type ELFCOMPRESS_ZSTD, ch_size 0x7fffffff00 and an 8-byte
payload: on an unfixed build libelf requests malloc (549755813632)
(512 GiB); on a fixed build the decompress fails with
ELF_E_INVALID_DATA and no oversized allocation is attempted.
Signed-off-by: Matej Smycka <[email protected]>
---
libelf/elf_compress.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/libelf/elf_compress.c b/libelf/elf_compress.c
index 4c14a561..e0784419 100644
--- a/libelf/elf_compress.c
+++ b/libelf/elf_compress.c
@@ -410,6 +410,12 @@ __libelf_decompress_zlib (void *buf_in, size_t size_in,
size_t size_out)
static void *
__libelf_decompress_zstd (void *buf_in, size_t size_in, size_t size_out)
{
+ if (unlikely (size_out / 32768 > size_in))
+ {
+ __libelf_seterrno (ELF_E_INVALID_DATA);
+ return NULL;
+ }
+
/* Malloc might return NULL when requesting zero size. This is highly
unlikely, it would only happen when the compression was forced.
But we do need a non-NULL buffer to return and set as result.
--
2.47.3