]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
libdw: Make sure all attributes come with a (fake) CU for bound checks.
authorMark Wielaard <mjw@redhat.com>
Fri, 12 Dec 2014 15:43:04 +0000 (16:43 +0100)
committerMark Wielaard <mjw@redhat.com>
Wed, 17 Dec 2014 15:35:56 +0000 (16:35 +0100)
All attributes now have a reference to a (fake) CU that has startp and
endp set to the data section where the form data comes from. Use that
for bounds checking in __libdw_form_val_len and dwarf_formblock to make
sure data read doesn't overflow any data section. Remove libdwP.h cu_data
and use cu startp and endp directly where appropriate.

Signed-off-by: Mark Wielaard <mjw@redhat.com>
14 files changed:
libdw/ChangeLog
libdw/dwarf_begin_elf.c
libdw/dwarf_child.c
libdw/dwarf_end.c
libdw/dwarf_formblock.c
libdw/dwarf_formref_die.c
libdw/dwarf_getattrs.c
libdw/dwarf_getlocation.c
libdw/dwarf_getlocation_attr.c
libdw/dwarf_getlocation_implicit_pointer.c
libdw/dwarf_getmacros.c
libdw/libdwP.h
libdw/libdw_findcu.c
libdw/libdw_form.c

index 4d08412b92dabb481877e5e515aa057ab5d62036..04ae613bc0f26b41bd73f2f0203a0c29bcf013c3 100644 (file)
@@ -1,3 +1,31 @@
+2014-12-12  Mark Wielaard  <mjw@redhat.com>
+
+       * libdwP.h (struct Dwarf): Add fake_loc_cu.
+       (cu_data): Removed.
+       (DIE_OFFSET_FROM_CU_OFFSET): Don't use cu_data, use cu_sec_idx.
+       (__libdw_form_val_compute_len): Drop dbg and endp arguments.
+       (__libdw_form_val_len): Likewise.
+       * libdw_form.c (__libdw_form_val_compute_len): Likewise.
+       * libdw_findcu.c (__libdw_intern_next_unit): Don't use cu_data, use
+       the already found data buffer directly.
+       * dwarf_begin_elf.c (valid_p): Setup fake_loc_cu.
+       * dwarf_end.c (dwarf_end): Free fake_loc_cu.
+       * dwarf_child.c (__libdw_find_attr): Call __libdw_form_val_len with
+       just cu.
+       * dwarf_getattrs.c (dwarf_getattrs): Likewise.
+       * dwarf_formblock.c (dwarf_formblock): Add bounds checking.
+       * dwarf_getlocation_attr.c (attr_form_cu): New function.
+       (dwarf_getlocation_attr): Use attr_form_cu to set result->cu.
+       (getlocation): Handle empty blocks immediately.
+       * dwarf_getlocation_implicit_pointer.c (empty_cu): New static var.
+       (__libdw_empty_loc_attr): Drop cu argument, use empty_cu.
+       (dwarf_getlocation_implicit_pointer): Call __libdw_empty_loc_attr with
+       one argument.
+       * dwarf_getmacros.c (read_macros): Also setup startp and endp for
+       fake_cu. Call __libdw_form_val_len with just fake_cu.
+       * dwarf_formref_die.c (dwarf_formref_die): Don't use cu_data, get
+       datap and size directly from cu startp and endp.
+
 2014-12-11  Mark Wielaard  <mjw@redhat.com>
 
        * libdw_findcu.c (__libdw_intern_next_unit): Sanity check offset.
index 4c6346ae7849b7aaf68374ab912c3c943019154b..4c49ce213ee35dcb62b0ca9cfa73724d55c07a08 100644 (file)
@@ -235,6 +235,28 @@ valid_p (Dwarf *result)
       result = NULL;
     }
 
+  if (result != NULL && result->sectiondata[IDX_debug_loc] != NULL)
+    {
+      result->fake_loc_cu = (Dwarf_CU *) calloc (1, sizeof (Dwarf_CU));
+      if (unlikely (result->fake_loc_cu == NULL))
+       {
+         __libdw_free_zdata (result);
+         Dwarf_Sig8_Hash_free (&result->sig8_hash);
+         __libdw_seterrno (DWARF_E_NOMEM);
+         free (result);
+         result = NULL;
+       }
+      else
+       {
+         result->fake_loc_cu->dbg = result;
+         result->fake_loc_cu->startp
+           = result->sectiondata[IDX_debug_loc]->d_buf;
+         result->fake_loc_cu->endp
+           = (result->sectiondata[IDX_debug_loc]->d_buf
+              + result->sectiondata[IDX_debug_loc]->d_size);
+       }
+    }
+
   return result;
 }
 
index 2a5d379d8ab1a25ade3daedbff152c030ab7b005..96d8e2342330f35ebecb54dcd3f86aa7d92dc218 100644 (file)
@@ -94,9 +94,7 @@ __libdw_find_attr (Dwarf_Die *die, unsigned int search_name,
       /* Skip over the rest of this attribute (if there is any).  */
       if (attr_form != 0)
        {
-         size_t len = __libdw_form_val_len (dbg, die->cu, attr_form, readp,
-                                            endp);
-
+         size_t len = __libdw_form_val_len (die->cu, attr_form, readp);
          if (unlikely (len == (size_t) -1l))
            {
              readp = NULL;
index 647a1b8d1c749954292e6578540da98233de06fd..922dc8f33dea090a534c009ebfdf6b65d41c60e7 100644 (file)
@@ -117,6 +117,9 @@ dwarf_end (dwarf)
       if (dwarf->free_elf)
        elf_end (dwarf->elf);
 
+      /* Free the fake location list CU.  */
+      free (dwarf->fake_loc_cu);
+
       /* Free the context descriptor.  */
       free (dwarf);
     }
index 799d8776df8b96095dfb7bea4d649b1915266d7d..980bc10f01cfad46b695998d9de893a278e6458f 100644 (file)
@@ -1,5 +1,5 @@
 /* Return block represented by attribute.
-   Copyright (C) 2004-2010 Red Hat, Inc.
+   Copyright (C) 2004-2010, 2014 Red Hat, Inc.
    This file is part of elfutils.
    Written by Ulrich Drepper <drepper@redhat.com>, 2004.
 
@@ -43,28 +43,37 @@ dwarf_formblock (attr, return_block)
   if (attr == NULL)
     return -1;
 
-  const unsigned char *datap;
+  const unsigned char *datap = attr->valp;
+  const unsigned char *endp = attr->cu->endp;
 
   switch (attr->form)
     {
     case DW_FORM_block1:
+      if (unlikely (endp - datap < 1))
+       goto invalid;
       return_block->length = *(uint8_t *) attr->valp;
       return_block->data = attr->valp + 1;
       break;
 
     case DW_FORM_block2:
+      if (unlikely (endp - datap < 2))
+       goto invalid;
       return_block->length = read_2ubyte_unaligned (attr->cu->dbg, attr->valp);
       return_block->data = attr->valp + 2;
       break;
 
     case DW_FORM_block4:
+      if (unlikely (endp - datap < 4))
+       goto invalid;
       return_block->length = read_4ubyte_unaligned (attr->cu->dbg, attr->valp);
       return_block->data = attr->valp + 4;
       break;
 
     case DW_FORM_block:
     case DW_FORM_exprloc:
-      datap = attr->valp;
+      if (unlikely (endp - datap < 1))
+       goto invalid;
+      // XXX bounds check
       get_uleb128 (return_block->length, datap);
       return_block->data = (unsigned char *) datap;
       break;
@@ -74,12 +83,10 @@ dwarf_formblock (attr, return_block)
       return -1;
     }
 
-  if (unlikely (cu_data (attr->cu)->d_size
-               - (return_block->data
-                  - (unsigned char *) cu_data (attr->cu)->d_buf)
-               < return_block->length))
+  if (unlikely (return_block->length > (size_t) (endp - return_block->data)))
     {
       /* Block does not fit.  */
+    invalid:
       __libdw_seterrno (DWARF_E_INVALID_DWARF);
       return -1;
     }
index b54e21667a3df1fc76b636ec090b161fc77986ef..63f669716e88f8d0d8a71a68e504d9046654eca2 100644 (file)
@@ -70,7 +70,8 @@ dwarf_formref_die (attr, result)
       return INTUSE(dwarf_offdie) (dbg_ret, offset, result);
     }
 
-  Elf_Data *data;
+  const unsigned char *datap;
+  size_t size;
   if (attr->form == DW_FORM_ref_sig8)
     {
       /* This doesn't have an offset, but instead a value we
@@ -92,7 +93,8 @@ dwarf_formref_die (attr, result)
          }
        while (cu->type_sig8 != sig);
 
-      data = cu->dbg->sectiondata[IDX_debug_types];
+      datap = cu->dbg->sectiondata[IDX_debug_types]->d_buf;
+      size = cu->dbg->sectiondata[IDX_debug_types]->d_size;
       offset = cu->type_offset;
     }
   else
@@ -101,17 +103,18 @@ dwarf_formref_die (attr, result)
       if (unlikely (__libdw_formref (attr, &offset) != 0))
        return NULL;
 
-      data = cu_data (cu);
+      datap = cu->startp;
+      size = cu->endp - cu->startp;
     }
 
-  if (unlikely (data->d_size - cu->start <= offset))
+  if (unlikely (offset >= size))
     {
       __libdw_seterrno (DWARF_E_INVALID_DWARF);
       return NULL;
     }
 
   memset (result, '\0', sizeof (Dwarf_Die));
-  result->addr = (char *) data->d_buf + cu->start + offset;
+  result->addr = (char *) datap + offset;
   result->cu = cu;
   return result;
 }
index 9ea70fca4068163137e44267d46312040cafd69b..0c54e5d0a93d0314969f7553389abec2347bd49d 100644 (file)
@@ -106,9 +106,7 @@ dwarf_getattrs (Dwarf_Die *die, int (*callback) (Dwarf_Attribute *, void *),
       /* Skip over the rest of this attribute (if there is any).  */
       if (attr.form != 0)
        {
-         size_t len = __libdw_form_val_len (dbg, die->cu, attr.form,
-                                            die_addr, endp);
-
+         size_t len = __libdw_form_val_len (die->cu, attr.form, die_addr);
          if (unlikely (len == (size_t) -1l))
            /* Something wrong with the file.  */
            return -1l;
index 2a4c8906f4a6eb1c5bc6861877d8e14836033658..38e93e683e3a4bbceb210281c954155ec5a3c618 100644 (file)
@@ -555,6 +555,14 @@ static int
 getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block,
             Dwarf_Op **llbuf, size_t *listlen, int sec_index)
 {
+  /* Empty location expressions don't have any ops to intern.
+     Note that synthetic empty_cu doesn't have an associated DWARF dbg.  */
+  if (block->length == 0)
+    {
+      *listlen = 0;
+      return 0;
+    }
+
   return __libdw_intern_expression (cu->dbg, cu->dbg->other_byte_order,
                                    cu->address_size, (cu->version == 2
                                                       ? cu->address_size
index cb290456be55cf237ad2c2867bb75b8809add237..3229baf624dc844dab920988d75059d93654c01e 100644 (file)
@@ -1,5 +1,5 @@
 /* Return DWARF attribute associated with a location expression op.
-   Copyright (C) 2013 Red Hat, Inc.
+   Copyright (C) 2013, 2014 Red Hat, Inc.
    This file is part of elfutils.
 
    This file is free software; you can redistribute it and/or modify
 #include <dwarf.h>
 #include <libdwP.h>
 
+static Dwarf_CU *
+attr_form_cu (Dwarf_Attribute *attr)
+{
+  /* If the attribute has block/expr form the data comes from the
+     .debug_info from the same cu as the attr.  Otherwise it comes from
+     the .debug_loc data section.  */
+  switch (attr->form)
+    {
+    case DW_FORM_block1:
+    case DW_FORM_block2:
+    case DW_FORM_block4:
+    case DW_FORM_block:
+    case DW_FORM_exprloc:
+      return attr->cu;
+    default:
+      return attr->cu->dbg->fake_loc_cu;
+    }
+}
 
 int
 dwarf_getlocation_attr (attr, op, result)
@@ -43,26 +61,27 @@ dwarf_getlocation_attr (attr, op, result)
   if (attr == NULL)
     return -1;
 
-  result->cu = attr->cu;
-
   switch (op->atom)
     {
       case DW_OP_implicit_value:
        result->code = DW_AT_const_value;
        result->form = DW_FORM_block;
        result->valp = (unsigned char *) (uintptr_t) op->number2;
+       result->cu = attr_form_cu (attr);
        break;
 
       case DW_OP_GNU_entry_value:
        result->code = DW_AT_location;
        result->form = DW_FORM_exprloc;
        result->valp = (unsigned char *) (uintptr_t) op->number2;
+       result->cu = attr_form_cu (attr);
        break;
 
       case DW_OP_GNU_const_type:
        result->code = DW_AT_const_value;
        result->form = DW_FORM_block1;
        result->valp = (unsigned char *) (uintptr_t) op->number2;
+       result->cu = attr_form_cu (attr);
        break;
 
       case DW_OP_call2:
@@ -74,7 +93,7 @@ dwarf_getlocation_attr (attr, op, result)
            return -1;
          if (INTUSE(dwarf_attr) (&die, DW_AT_location, result) == NULL)
            {
-             __libdw_empty_loc_attr (result, attr->cu);
+             __libdw_empty_loc_attr (result);
              return 0;
            }
        }
@@ -88,7 +107,7 @@ dwarf_getlocation_attr (attr, op, result)
          if (INTUSE(dwarf_attr) (&die, DW_AT_location, result) == NULL
              && INTUSE(dwarf_attr) (&die, DW_AT_const_value, result) == NULL)
            {
-             __libdw_empty_loc_attr (result, attr->cu);
+             __libdw_empty_loc_attr (result);
              return 0;
            }
        }
index f93d17ec8b63766744bc1ea7d618e6dd3d9c3cc8..f1c16be7783a628f65fea50b08d9f09771d8cdec 100644 (file)
 
 
 static unsigned char empty_exprloc = 0;
+static Dwarf_CU empty_cu = { .startp = &empty_exprloc,
+                            .endp = &empty_exprloc + 1 };
 
 void
 internal_function
-__libdw_empty_loc_attr (Dwarf_Attribute *attr, struct Dwarf_CU *cu )
+__libdw_empty_loc_attr (Dwarf_Attribute *attr)
 {
   attr->code = DW_AT_location;
   attr->form = DW_FORM_exprloc;
   attr->valp = &empty_exprloc;
-  attr->cu = cu;
+  attr->cu = &empty_cu;
 }
 
 int
@@ -69,7 +71,7 @@ dwarf_getlocation_implicit_pointer (attr, op, result)
   if (INTUSE(dwarf_attr) (&die, DW_AT_location, result) == NULL
       && INTUSE(dwarf_attr) (&die, DW_AT_const_value, result) == NULL)
     {
-      __libdw_empty_loc_attr (result, attr->cu);
+      __libdw_empty_loc_attr (result);
       return 0;
     }
 
index 737dc5d110d694f7520cb86c2184a3accfab621d..848128e2c7134a3e43e5d40ac027701ecbb2221c 100644 (file)
@@ -354,6 +354,8 @@ read_macros (Dwarf *dbg, int sec_index,
        .dbg = dbg,
        .version = 4,
        .offset_size = table->is_64bit ? 8 : 4,
+       .startp = (void *) startp + offset,
+       .endp = (void *) endp,
       };
 
       Dwarf_Attribute attributes[proto->nforms];
@@ -367,8 +369,7 @@ read_macros (Dwarf *dbg, int sec_index,
          attributes[i].valp = (void *) readp;
          attributes[i].cu = &fake_cu;
 
-         size_t len = __libdw_form_val_len (dbg, &fake_cu,
-                                            proto->forms[i], readp, endp);
+         size_t len = __libdw_form_val_len (&fake_cu, proto->forms[i], readp);
          if (len == (size_t) -1)
            return -1;
 
index edceb59a091df556fd35bfd8b8e684c004f4bc03..eda61a97d54efebab264f8c7ee2bea7720a25179 100644 (file)
@@ -188,6 +188,10 @@ struct Dwarf
   /* Cached info from the CFI section.  */
   struct Dwarf_CFI_s *cfi;
 
+  /* Fake loc CU.  Used when synthesizing attributes for Dwarf_Ops that
+     came from a location list entry in dwarf_getlocation_attr.  */
+  struct Dwarf_CU *fake_loc_cu;
+
   /* Internal memory handling.  This is basically a simplified
      reimplementation of obstacks.  Unfortunately the standard obstack
      implementation is not usable in libraries.  */
@@ -337,7 +341,7 @@ struct Dwarf_CU
   ((Dwarf_Die)                                                               \
    {                                                                         \
      .cu = (fromcu),                                                         \
-     .addr = ((char *) cu_data (fromcu)->d_buf                               \
+     .addr = ((char *) fromcu->dbg->sectiondata[cu_sec_idx (fromcu)]->d_buf   \
              + DIE_OFFSET_FROM_CU_OFFSET ((fromcu)->start,                   \
                                           (fromcu)->offset_size,             \
                                           (fromcu)->type_offset != 0))       \
@@ -483,18 +487,16 @@ __libdw_dieabbrev (Dwarf_Die *die, const unsigned char **readp)
 }
 
 /* Helper functions for form handling.  */
-extern size_t __libdw_form_val_compute_len (Dwarf *dbg, struct Dwarf_CU *cu,
+extern size_t __libdw_form_val_compute_len (struct Dwarf_CU *cu,
                                            unsigned int form,
-                                           const unsigned char *valp,
-                                           const unsigned char *endp)
-     __nonnull_attribute__ (1, 2, 4, 5) internal_function;
+                                           const unsigned char *valp)
+     __nonnull_attribute__ (1, 3) internal_function;
 
 /* Find the length of a form attribute.  */
 static inline size_t
-__nonnull_attribute__ (1, 2, 4, 5)
-__libdw_form_val_len (Dwarf *dbg, struct Dwarf_CU *cu,
-                     unsigned int form, const unsigned char *valp,
-                     const unsigned char *endp)
+__nonnull_attribute__ (1, 3)
+__libdw_form_val_len (struct Dwarf_CU *cu, unsigned int form,
+                     const unsigned char *valp)
 {
   /* Small lookup table of forms with fixed lengths.  Absent indexes are
      initialized 0, so any truly desired 0 is set to 0x80 and masked.  */
@@ -513,6 +515,7 @@ __libdw_form_val_len (Dwarf *dbg, struct Dwarf_CU *cu,
       uint8_t len = form_lengths[form];
       if (len != 0)
        {
+         const unsigned char *endp = cu->endp;
          len &= 0x7f; /* Mask to allow 0x80 -> 0.  */
          if (unlikely (len > (size_t) (endp - valp)))
            {
@@ -524,7 +527,7 @@ __libdw_form_val_len (Dwarf *dbg, struct Dwarf_CU *cu,
     }
 
   /* Other forms require some computation.  */
-  return __libdw_form_val_compute_len (dbg, cu, form, valp, endp);
+  return __libdw_form_val_compute_len (cu, form, valp);
 }
 
 /* Helper function for DW_FORM_ref* handling.  */
@@ -721,12 +724,6 @@ cu_sec_idx (struct Dwarf_CU *cu)
   return cu->type_offset == 0 ? IDX_debug_info : IDX_debug_types;
 }
 
-static inline Elf_Data *
-cu_data (struct Dwarf_CU *cu)
-{
-  return cu->dbg->sectiondata[cu_sec_idx (cu)];
-}
-
 /* Read up begin/end pair and increment read pointer.
     - If it's normal range record, set up *BEGINP and *ENDP and return 0.
     - If it's base address selection record, set up *BASEP and return 1.
@@ -744,7 +741,7 @@ unsigned char * __libdw_formptr (Dwarf_Attribute *attr, int sec_index,
   internal_function;
 
 /* Fills in the given attribute to point at an empty location expression.  */
-void __libdw_empty_loc_attr (Dwarf_Attribute *attr, struct Dwarf_CU *cu)
+void __libdw_empty_loc_attr (Dwarf_Attribute *attr)
   internal_function;
 
 /* Load .debug_line unit at DEBUG_LINE_OFFSET.  COMP_DIR is a value of
index c783ff8a5340c6592a2cf9be5ae6e1c5d84d7d11..d8da2e38af41ac3eb59452beb8761e26d419f792 100644 (file)
@@ -119,9 +119,8 @@ __libdw_intern_next_unit (dbg, debug_types)
   if (debug_types)
     Dwarf_Sig8_Hash_insert (&dbg->sig8_hash, type_sig8, newp);
 
-  void *buf = cu_data (newp)->d_buf;
-  newp->startp = buf + newp->start;
-  newp->endp = buf + newp->end;
+  newp->startp = data->d_buf + newp->start;
+  newp->endp = data->d_buf + newp->end;
 
   /* Add the new entry to the search tree.  */
   if (tsearch (newp, tree, findcu_cb) == NULL)
index 65ac7dede5c4493309cc07b9529f36a90da7e928..379ede509863b2d924bad6f9853dc48acf146643 100644 (file)
 
 size_t
 internal_function
-__libdw_form_val_compute_len (Dwarf *dbg, struct Dwarf_CU *cu,
-                             unsigned int form, const unsigned char *valp,
-                             const unsigned char *endp)
+__libdw_form_val_compute_len (struct Dwarf_CU *cu, unsigned int form,
+                             const unsigned char *valp)
 {
   const unsigned char *startp = valp;
+  const unsigned char *endp = cu->endp;
   Dwarf_Word u128;
   size_t result;
 
@@ -75,13 +75,13 @@ __libdw_form_val_compute_len (Dwarf *dbg, struct Dwarf_CU *cu,
     case DW_FORM_block2:
       if (unlikely ((size_t) (endp - startp) < 2))
        goto invalid;
-      result = read_2ubyte_unaligned (dbg, valp) + 2;
+      result = read_2ubyte_unaligned (cu->dbg, valp) + 2;
       break;
 
     case DW_FORM_block4:
       if (unlikely ((size_t) (endp - startp) < 4))
        goto invalid;
-      result = read_4ubyte_unaligned (dbg, valp) + 4;
+      result = read_4ubyte_unaligned (cu->dbg, valp) + 4;
       break;
 
     case DW_FORM_block:
@@ -112,7 +112,7 @@ __libdw_form_val_compute_len (Dwarf *dbg, struct Dwarf_CU *cu,
     case DW_FORM_indirect:
       get_uleb128 (u128, valp);
       // XXX Is this really correct?
-      result = __libdw_form_val_len (dbg, cu, u128, valp, endp);
+      result = __libdw_form_val_len (cu, u128, valp);
       if (result != (size_t) -1)
        result += valp - startp;
       else