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.
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.
Add the same "bytes >= limit" check that the surrounding code already
uses for the other reads in this function before consuming the encoding
byte.
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;
--
2.52.0