summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wielaard <mark@klomp.org>2019-11-11 00:15:55 +0100
committerMark Wielaard <mark@klomp.org>2019-11-12 22:33:56 +0100
commit287e502815bf133f64afbf47211b11364a0a322f (patch)
tree37b5cc5d828ac08ff0e90a8db2078a708573f005
parent32cd9c4ad5b888da9c4f94a702a02d380b2a745b (diff)
downloadelfutils-287e502815bf133f64afbf47211b11364a0a322f.tar.gz
libdw: Introduce libdw_unalloc to stop Dwarf_Abbrev leaks.
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>
-rw-r--r--libdw/ChangeLog9
-rw-r--r--libdw/dwarf_getabbrev.c10
-rw-r--r--libdw/libdwP.h13
-rw-r--r--libdw/libdw_alloc.c12
4 files changed, 43 insertions, 1 deletions
diff --git a/libdw/ChangeLog b/libdw/ChangeLog
index d308172b..95ac28a7 100644
--- a/libdw/ChangeLog
+++ b/libdw/ChangeLog
@@ -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.
diff --git a/libdw/dwarf_getabbrev.c b/libdw/dwarf_getabbrev.c
index 7e767fc1..13bee493 100644
--- a/libdw/dwarf_getabbrev.c
+++ b/libdw/dwarf_getabbrev.c
@@ -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;
diff --git a/libdw/libdwP.h b/libdw/libdwP.h
index 3e1ef59b..36c2acd9 100644
--- a/libdw/libdwP.h
+++ b/libdw/libdwP.h
@@ -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);
diff --git a/libdw/libdw_alloc.c b/libdw/libdw_alloc.c
index 0eb02c34..e0281a3d 100644
--- a/libdw/libdw_alloc.c
+++ b/libdw/libdw_alloc.c
@@ -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)
{