libdw: Introduce libdw_unalloc to stop Dwarf_Abbrev leaks.
authorMark Wielaard <mark@klomp.org>
Sun, 10 Nov 2019 23:15:55 +0000 (00:15 +0100)
committerMark Wielaard <mark@klomp.org>
Tue, 12 Nov 2019 21:33:56 +0000 (22:33 +0100)
In the case of reading an invalid abbrev or when reading an abbrev
concurrently the Dwarf_Abbrev just created might leak because it isn't
needed after all. Introduce libdw_unalloc and libdw_typed_unalloc to
unallocate such Dwarf_Abbrevs so they don't leak.

Signed-off-by: Mark Wielaard <mark@klomp.org>
libdw/ChangeLog
libdw/dwarf_getabbrev.c
libdw/libdwP.h
libdw/libdw_alloc.c

index d308172..95ac28a 100644 (file)
@@ -1,3 +1,12 @@
+2019-11-10  Mark Wielaard  <mark@klomp.org>
+
+       * libdwP.h (libdw_unalloc): New define.
+       (libdw_typed_unalloc): Likewise.
+       (__libdw_thread_tail): New function declaration.
+       * libdw_alloc.c (__libdw_thread_tail): New function.
+       * dwarf_getabbrev.c (__libdw_getabbrev): Call libdw_typed_unalloc
+       when reading invalid data or when hash collission detected.
+
 2019-10-28  Jonathon Anderson  <jma14@rice.edu>
 
        * libdw_alloc.c: Added __libdw_alloc_tail.
index 7e767fc..13bee49 100644 (file)
@@ -99,6 +99,8 @@ __libdw_getabbrev (Dwarf *dbg, struct Dwarf_CU *cu, Dwarf_Off offset,
          /* A duplicate abbrev code at a different offset,
             that should never happen.  */
        invalid:
+         if (! foundit)
+           libdw_typed_unalloc (dbg, Dwarf_Abbrev);
          __libdw_seterrno (DWARF_E_INVALID_DWARF);
          return NULL;
        }
@@ -148,7 +150,13 @@ __libdw_getabbrev (Dwarf *dbg, struct Dwarf_CU *cu, Dwarf_Off offset,
 
   /* Add the entry to the hash table.  */
   if (cu != NULL && ! foundit)
-    (void) Dwarf_Abbrev_Hash_insert (&cu->abbrev_hash, abb->code, abb);
+    if (Dwarf_Abbrev_Hash_insert (&cu->abbrev_hash, abb->code, abb) == -1)
+      {
+       /* The entry was already in the table, remove the one we just
+          created and get the one already inserted.  */
+       libdw_typed_unalloc (dbg, Dwarf_Abbrev);
+       abb = Dwarf_Abbrev_Hash_find (&cu->abbrev_hash, code);
+      }
 
  out:
   return abb;
index 3e1ef59..36c2acd 100644 (file)
@@ -599,10 +599,23 @@ extern void __libdw_seterrno (int value) internal_function;
 #define libdw_typed_alloc(dbg, type) \
   libdw_alloc (dbg, type, sizeof (type), 1)
 
+/* Can only be used to undo the last libdw_alloc.  */
+#define libdw_unalloc(dbg, type, tsize, cnt) \
+  ({ struct libdw_memblock *_tail = __libdw_thread_tail (dbg);               \
+     size_t _required = (tsize) * (cnt);                                     \
+     /* We cannot know the padding, it is lost.  */                          \
+     _tail->remaining += _required; })                                       \
+
+#define libdw_typed_unalloc(dbg, type) \
+  libdw_unalloc (dbg, type, sizeof (type), 1)
+
 /* Callback to choose a thread-local memory allocation stack.  */
 extern struct libdw_memblock *__libdw_alloc_tail (Dwarf* dbg)
      __nonnull_attribute__ (1);
 
+extern struct libdw_memblock *__libdw_thread_tail (Dwarf* dbg)
+     __nonnull_attribute__ (1);
+
 /* Callback to allocate more.  */
 extern void *__libdw_allocate (Dwarf *dbg, size_t minsize, size_t align)
      __attribute__ ((__malloc__)) __nonnull_attribute__ (1);
index 0eb02c3..e0281a3 100644 (file)
@@ -97,6 +97,18 @@ __libdw_alloc_tail (Dwarf *dbg)
   return result;
 }
 
+/* Can only be called after a allocation for this thread has already
+   been done, to possibly undo it.  */
+struct libdw_memblock *
+__libdw_thread_tail (Dwarf *dbg)
+{
+  struct libdw_memblock *result;
+  pthread_rwlock_rdlock (&dbg->mem_rwl);
+  result = dbg->mem_tails[thread_id];
+  pthread_rwlock_unlock (&dbg->mem_rwl);
+  return result;
+}
+
 void *
 __libdw_allocate (Dwarf *dbg, size_t minsize, size_t align)
 {