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

Reply via email to