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
> 

Reply via email to