Hi Matej,
On Mon, Sep 07, 2026 at 12:34:19AM +0200, Matej Smycka wrote:
> __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.
This sound like a good idea and 32768:1 might be a good ratio.
But is there a specific reason you picked this? The zlib limit says:
/* Catch highly unlikely compression ratios so we don't allocate
some giant amount of memory for nothing. The max compression
factor 1032:1 comes from http://www.zlib.net/zlib_tech.html */
Is there some similar technical limit for zstd?
Thanks,
Mark
> 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
>