Hi Sayed,

On Sun, Jun 28, 2026 at 08:29:01PM +0530, Sayed Kaif wrote:
> dwarf_next_cfi parses CFI entries directly from the (untrusted)
> .eh_frame or .debug_frame section data.  When a CIE uses a sized ('z')
> augmentation, the loop over the augmentation string characters consumes
> an encoding byte for the 'L' and 'P' directives with
> 
>     encoding = *bytes++;
> 
> without first checking that bytes is still within the entry.  The only
> bounds check (bytes > augmentation_data + augmentation_data_size) is done
> after the loop, so the read happens before it is validated.

And so if it isn't the last entry we detect the invalid data. But if...

> A crafted CIE with augmentation "zL" (or "zP") and an empty augmentation
> data area leaves bytes pointing exactly at the end of the section, so
> *bytes++ reads one byte past the section buffer.  When the malformed CIE
> is the last entry in the section this is a read past the end of the
> mapped data.

... then we did the invalid read before the sanity check and we do a
possible illegal derefence.

Nicely spotted.

> Add the same "bytes >= limit" check that the surrounding code already
> uses for the other reads in this function before consuming the encoding
> byte.

Yes, that seems the right guard to use here.
 
> The problem was found with AddressSanitizer.  A 16-byte .eh_frame
> containing a single CIE
> 
>     0c 00 00 00   length = 12
>     00 00 00 00   CIE_id = 0
>     01            version = 1
>     7a 4c 00      augmentation = "zL"
>     01            code_alignment_factor = 1
>     7f            data_alignment_factor = -1
>     00            return_address_register = 0
>     00            augmentation_data_size = 0
> 
> makes dwarf_next_cfi read one byte past the end:
> 
>     ERROR: AddressSanitizer: heap-buffer-overflow ... READ of size 1
>         #0 dwarf_next_cfi libdw/dwarf_next_cfi.c:219
> 
> With the fix dwarf_next_cfi returns DWARF_E_INVALID_DWARF for that input,
> and parsing of the .eh_frame data of normal binaries is unchanged
> (libc.so, gcc and libelf.so all iterate without errors).
> 
> Signed-off-by: Sayed Kaif <[email protected]>
> ---
> diff --git a/libdw/dwarf_next_cfi.c b/libdw/dwarf_next_cfi.c
> index be08984..5bb74b8 100644
> --- a/libdw/dwarf_next_cfi.c
> +++ b/libdw/dwarf_next_cfi.c
> @@ -216,6 +216,8 @@ dwarf_next_cfi (const unsigned char e_ident[],
>             if (sized_augmentation)
>               {
>                 /* Skip LSDA pointer encoding byte.  */
> +               if (bytes >= limit)
> +                 goto invalid;
>                 encoding = *bytes++;
>                 entry->cie.fde_augmentation_data_size
>                   += encoded_value_size (data, e_ident, encoding, NULL);
> @@ -234,6 +236,8 @@ dwarf_next_cfi (const unsigned char e_ident[],
>             if (sized_augmentation)
>               {
>                 /* Skip encoded personality routine pointer. */
> +               if (bytes >= limit)
> +                 goto invalid;
>                 encoding = *bytes++;
>                 bytes += encoded_value_size (data, e_ident, encoding, bytes);
>                 continue;

OK. And the 'R' case doesn't need a limit check because it doesn't
actually try to read the byte.

Thanks, will push.

Cheers,

Mark

Reply via email to