]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
libdw, readelf, elflint: Add get_(u|s)leb128 guards
authorMark Wielaard <mark@klomp.org>
Wed, 8 Feb 2023 15:53:24 +0000 (16:53 +0100)
committerMark Wielaard <mark@klomp.org>
Tue, 14 Feb 2023 15:45:10 +0000 (16:45 +0100)
Add sanity check making sure an leb128 isn't being read beyond the
end of the current data segment. Most code already had these guards,
but some were missing. This makes sure an appropriate error is
generated instead.

Signed-off-by: Mark Wielaard <mark@klomp.org>
13 files changed:
libdw/ChangeLog
libdw/cfi.c
libdw/dwarf_child.c
libdw/dwarf_frame_register.c
libdw/dwarf_getabbrev.c
libdw/dwarf_getlocation.c
libdw/dwarf_getsrclines.c
libdw/encoded-value.h
libdw/fde.c
libdw/libdw_form.c
src/ChangeLog
src/elflint.c
src/readelf.c

index 71e96c880b739549cbd5f5e166428ed58d786273..2c1f61e899b556e5b136ad7c8f055d0dfdd39b13 100644 (file)
@@ -1,3 +1,19 @@
+2023-02-12  Mark Wielaard  <mark@klomp.org>
+
+       * cfi.c (execute_cfi): Add cfi_asser before reading second lib128.
+       * dwarf_chld.c (__libdw_find_attr): Check readp >= endp before
+       calling get_uleb128.
+       * dwarf_frame_register.c (dwarf_frame_register): Likewise for
+       p >= end.
+       * dwarf_getabbrev.c (__libdw_getabbrev): Add comment about check.
+       * dwarf_getlocation.c (__libdw_intern_expression): Update check to
+       account for both the number and uleb128.
+       * encoded-value.h (read_encoded_value): Check p >= end for
+       DW_EH_PE_(u|s)leb128.
+       * fde.c (intern_fde): Check len can be read as uleb128.
+       * libdw_form.c (__libdw_form_val_compute_len): Check valp >= endp
+       before get_uleb128.
+
 2023-01-22  Mark Wielaard  <mark@klomp.org>
 
        * dwarf_getscopes.c (pc_record): Return nscopes when done.
index 6d08ca90d87df1135e80aaed3e99d5ffc6f5a68b..a71744056459d9cbea1b3c3325285664174bad74 100644 (file)
@@ -239,6 +239,7 @@ execute_cfi (Dwarf_CFI *cache,
 
        case DW_CFA_offset_extended_sf:
          get_uleb128 (operand, program, end);
+         cfi_assert (program < end);
          get_sleb128 (sf_offset, program, end);
        offset_extended_sf:
          offset = sf_offset * cie->data_alignment_factor;
@@ -294,6 +295,7 @@ execute_cfi (Dwarf_CFI *cache,
          get_uleb128 (regno, program, end);
          /* DW_FORM_block is a ULEB128 length followed by that many bytes.  */
          offset = program - (const uint8_t *) cache->data->d.d_buf;
+         cfi_assert (program < end);
          get_uleb128 (operand, program, end);
          cfi_assert (operand <= (Dwarf_Word) (end - program));
          program += operand;
index c8c8bb613a4bddfb2e40aeab2f09e7ca626cf05d..96a0d608bcaa21d54f0a935d9ca03b5de36bea5d 100644 (file)
@@ -73,10 +73,13 @@ __libdw_find_attr (Dwarf_Die *die, unsigned int search_name,
 
       if (attr_form == DW_FORM_indirect)
        {
+         if (readp >= endp)
+           goto invalid;
          get_uleb128 (attr_form, readp, endp);
          if (attr_form == DW_FORM_indirect ||
              attr_form == DW_FORM_implicit_const)
            {
+           invalid:
              __libdw_seterrno (DWARF_E_INVALID_DWARF);
              return NULL;
            }
index bcf3fa03045910e040c56226972d06bfaea2f0f6..a6b7c4c1a373d4b657305df6b43d23f2d9b20d66 100644 (file)
@@ -100,6 +100,11 @@ dwarf_frame_register (Dwarf_Frame *fs, int regno, Dwarf_Op ops_mem[3],
        const uint8_t *p = fs->cache->data->d.d_buf + reg->value;
        const uint8_t *end = (fs->cache->data->d.d_buf
                              + fs->cache->data->d.d_size);
+       if (p >= end)
+         {
+           __libdw_seterrno (DWARF_E_INVALID_DWARF);
+           return -1;
+         }
        get_uleb128 (block.length, p, end);
        block.data = (void *) p;
 
index 13bee493dfa4a5e7b738af9011d526ff724ae512..5b02333f34677e22c05dd899ae2295e33a1e04f2 100644 (file)
@@ -77,6 +77,7 @@ __libdw_getabbrev (Dwarf *dbg, struct Dwarf_CU *cu, Dwarf_Off offset,
                              + dbg->sectiondata[IDX_debug_abbrev]->d_size);
   const unsigned char *start_abbrevp = abbrevp;
   unsigned int code;
+  // We start off with abbrevp at offset, which is checked above.
   get_uleb128 (code, abbrevp, end);
 
   /* Check whether this code is already in the hash table.  */
index d0d7816319695f816ae41adbf86ff954d072eff2..4e8c047b5dd45496c66eb29a29ef6d1afd81ebeb 100644 (file)
@@ -545,7 +545,7 @@ __libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
        case DW_OP_deref_type:
        case DW_OP_GNU_deref_type:
        case DW_OP_xderef_type:
-         if (unlikely (data + 1 >= end_data))
+         if (unlikely (data + 2 >= end_data))
            goto invalid;
          newloc->number = *data++;
          get_uleb128 (newloc->number2, data, end_data);
index 2c1d7a407a078e50187481eae720da9bf887a587..df003c5fd69a71fa9e316ee6cd9ef628373be588 100644 (file)
@@ -572,6 +572,8 @@ read_srclines (Dwarf *dbg,
        goto invalid_data;
 
       size_t nfiles;
+      if ((size_t) (lineendp - linep) < 1)
+       goto invalid_data;
       get_uleb128 (nfiles, linep, lineendp);
 
       if (nforms == 0 && nfiles != 0)
index d4e01924d6f74f434a28a7de658fb9cca92469dc..4566ef96e0c7a89d7e7f21a9c715c8b863ea5da2 100644 (file)
@@ -196,10 +196,14 @@ read_encoded_value (const Dwarf_CFI *cache, uint8_t encoding,
       break;
 
     case DW_EH_PE_uleb128:
+      if (*p >= endp)
+       goto invalid_data;
       get_uleb128 (value, *p, endp);
       break;
 
     case DW_EH_PE_sleb128:
+      if (*p >= endp)
+       goto invalid_data;
       get_sleb128 (value, *p, endp);
       break;
 
index f5f6fbe14133b0cb36738e9c25a5cdfeaf3d9e8b..73d551b64d5af3d2ed85a56fdc4a6b72bfc6f046 100644 (file)
@@ -104,9 +104,12 @@ intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry)
       /* The CIE augmentation says the FDE has a DW_FORM_block
         before its actual instruction stream.  */
       Dwarf_Word len;
+      if (fde->instructions >= fde->instructions_end)
+       goto invalid;
       get_uleb128 (len, fde->instructions, fde->instructions_end);
       if ((Dwarf_Word) (fde->instructions_end - fde->instructions) < len)
        {
+       invalid:
          free (fde);
          __libdw_seterrno (DWARF_E_INVALID_DWARF);
          return NULL;
index c83dfb397f5c3780041ac4ff68cd6d660b1a3fdf..400454401b66ce298b8bdab643b14798eb6f0a56 100644 (file)
@@ -88,6 +88,8 @@ __libdw_form_val_compute_len (struct Dwarf_CU *cu, unsigned int form,
 
     case DW_FORM_block:
     case DW_FORM_exprloc:
+      if (valp >= endp)
+       goto invalid;
       get_uleb128 (u128, valp, endp);
       result = u128 + (valp - startp);
       break;
@@ -111,6 +113,8 @@ __libdw_form_val_compute_len (struct Dwarf_CU *cu, unsigned int form,
     case DW_FORM_strx:
     case DW_FORM_GNU_addr_index:
     case DW_FORM_GNU_str_index:
+      if (valp >= endp)
+       goto invalid;
       get_uleb128 (u128, valp, endp);
       result = valp - startp;
       break;
@@ -119,6 +123,8 @@ __libdw_form_val_compute_len (struct Dwarf_CU *cu, unsigned int form,
       /* The amount of data to skip in the DIE is the size of the actual
         FORM data (which is __libdw_form_val_len) plus the size of the
         uleb128 encoding that FORM (which is valp - startp).  */
+      if (valp >= endp)
+       goto invalid;
       get_uleb128 (u128, valp, endp);
       if (*valp == DW_FORM_indirect || *valp == DW_FORM_implicit_const)
        return (size_t) -1;
index 915494f2946bd233c333a88a9886c88e21079d95..699d98ee84bb7f89287699872e783a5d82c0970a 100644 (file)
@@ -1,3 +1,15 @@
+2023-02-12  Mark Wielaard  <mark@klomp.org>
+
+       * readelf.c (print_attributes): Add comment about check.
+       (read_encoded): Check readp >= endp before reading
+       DW_EH_PE_uleb128 and DW_EH_PE_sleb128.
+       * elflint.c (check_attributes): Check r >= q before reading
+       uleb128.
+       (print_debug_frame_section): Check augmentation length can be read
+       as uleb128.
+       (print_debug_exception_table): Likewise for ttype_base_offset,
+       call_site_table_len and action.
+
 2023-01-22  Mark Wielaard  <mark@klomp.org>
 
        * addr2line.c (options): Separate --demangle and -C.
index b4eac32f5e774092d59b4f88c8ea17e58f898c96..dd42dcb40e19503a9114ba291532667d5426bef8 100644 (file)
@@ -3569,9 +3569,12 @@ section [%2d] '%s': offset %zu: attribute subsection has unexpected tag %u\n"),
                    const unsigned char *r = chunk;
                    if (tag == 32 || (tag & 1) == 0)
                      {
+                       if (r >= q)
+                         goto invalid_uleb;
                        get_uleb128 (value, r, q);
                        if (r > q)
                          {
+                         invalid_uleb:
                            ERROR (_("\
 section [%2d] '%s': offset %zu: endless ULEB128 in attribute tag\n"),
                                   idx, section_name (ebl, idx), buffer_pos (data, chunk));
index 5b3319c2f3b55f32c45593c0280a5344c2696e6f..0f13874f537d4cd9f2e4fd32924761ce2621f2d5 100644 (file)
@@ -3802,6 +3802,7 @@ print_attributes (Ebl *ebl, const GElf_Ehdr *ehdr)
                        if (tag == 32 || (tag & 1) == 0
                            || (! gnu_vendor && (tag > 5 && tag < 32)))
                          {
+                           // Note r >= q check above.
                            get_uleb128 (value, r, q);
                            if (r > q)
                              break;
@@ -6368,9 +6369,13 @@ read_encoded (unsigned int encoding, const unsigned char *readp,
   switch (encoding & 0xf)
     {
     case DW_EH_PE_uleb128:
+      if (readp >= endp)
+       goto invalid;
       get_uleb128 (*res, readp, endp);
       break;
     case DW_EH_PE_sleb128:
+      if (readp >= endp)
+       goto invalid;
       get_sleb128 (*res, readp, endp);
       break;
     case DW_EH_PE_udata2:
@@ -6983,6 +6988,9 @@ print_debug_frame_section (Dwfl_Module *dwflmod, Ebl *ebl, GElf_Ehdr *ehdr,
 
          if (augmentation[0] == 'z')
            {
+             if (cieend - readp < 1)
+               goto invalid_data;
+
              unsigned int augmentationlen;
              get_uleb128 (augmentationlen, readp, cieend);
 
@@ -11010,6 +11018,8 @@ print_debug_exception_table (Dwfl_Module *dwflmod __attribute__ ((unused)),
   if (ttype_encoding != DW_EH_PE_omit)
     {
       unsigned int ttype_base_offset;
+      if (readp >= dataend)
+       goto invalid_data;
       get_uleb128 (ttype_base_offset, readp, dataend);
       printf (" TType base offset:   %#x\n", ttype_base_offset);
       if ((size_t) (dataend - readp) > ttype_base_offset)
@@ -11022,6 +11032,8 @@ print_debug_exception_table (Dwfl_Module *dwflmod __attribute__ ((unused)),
   printf (_(" Call site encoding:  %#x "), call_site_encoding);
   print_encoding_base ("", call_site_encoding);
   unsigned int call_site_table_len;
+  if (readp >= dataend)
+    goto invalid_data;
   get_uleb128 (call_site_table_len, readp, dataend);
 
   const unsigned char *const action_table = readp + call_site_table_len;
@@ -11044,6 +11056,8 @@ print_debug_exception_table (Dwfl_Module *dwflmod __attribute__ ((unused)),
       readp = read_encoded (call_site_encoding, readp, dataend,
                            &landing_pad, dbg);
       unsigned int action;
+      if (readp >= dataend)
+       goto invalid_data;
       get_uleb128 (action, readp, dataend);
       max_action = MAX (action, max_action);
       printf (_(" [%4u] Call site start:   %#" PRIx64 "\n"