Commit 6c801dd7 ("libdw: Only read CIE augmentation data if it is
there", bug 34387) made intern_new_cie only read the 'L'/'R'/'P'
encoding bytes for a sized ('z') augmentation. Those reads are still
driven purely by the augmentation-string characters and are not bounded
by the recorded augmentation_data_size: an augmentation string that
names more encoding bytes than the CIE's augmentation data actually
contains makes "*data++" read beyond info->augmentation_data +
info->augmentation_data_size.
Record the end of the augmentation data and reject the CIE with
DWARF_E_INVALID_DWARF before each read that would go past it, so the
augmentation-string parsing can never read outside the declared
augmentation data. This is the intern_new_cie counterpart to the limit
check dwarf_next_cfi already applies, as suggested on the list.
Signed-off-by: Matej Smycka <[email protected]>
---
Hi Mark,
You're right, my patch predated 6c801dd7, sorry for it.
Sending the rebased patch below.
libdw/cie.c | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/libdw/cie.c b/libdw/cie.c
index 052a704e..1e703fef 100644
--- a/libdw/cie.c
+++ b/libdw/cie.c
@@ -74,6 +74,7 @@ intern_new_cie (Dwarf_CFI *cache, Dwarf_Off offset, const
Dwarf_CIE *info)
/* Grok the augmentation string and its data. */
const uint8_t *data = info->augmentation_data;
+ const uint8_t *const limit = data + info->augmentation_data_size;
const char *ap = info->augmentation;
/* If present, 'z' must be the first char. */
if (*ap == 'z')
@@ -92,7 +93,11 @@ intern_new_cie (Dwarf_CFI *cache, Dwarf_Off offset, const
Dwarf_CIE *info)
case 'L': /* LSDA pointer encoding byte. */
if (cie->sized_augmentation_data)
- cie->lsda_encoding = *data++;
+ {
+ if (data >= limit)
+ goto invalid;
+ cie->lsda_encoding = *data++;
+ }
if (!cie->sized_augmentation_data)
cie->fde_augmentation_data_size
+= encoded_value_size (&cache->data->d, cache->e_ident,
@@ -101,12 +106,18 @@ intern_new_cie (Dwarf_CFI *cache, Dwarf_Off offset, const
Dwarf_CIE *info)
case 'R': /* FDE address encoding byte. */
if (cie->sized_augmentation_data)
- cie->fde_encoding = *data++;
+ {
+ if (data >= limit)
+ goto invalid;
+ cie->fde_encoding = *data++;
+ }
continue;
case 'P': /* Skip personality routine. */
if (cie->sized_augmentation_data)
{
+ if (data >= limit)
+ goto invalid;
encoding = *data++;
data += encoded_value_size (&cache->data->d, cache->e_ident,
encoding, data);
@@ -160,6 +171,11 @@ intern_new_cie (Dwarf_CFI *cache, Dwarf_Off offset, const
Dwarf_CIE *info)
}
return cie;
+
+ invalid:
+ free (cie);
+ __libdw_seterrno (DWARF_E_INVALID_DWARF);
+ return NULL;
}
/* Look up a CIE_pointer for random access. */
--
2.47.3