From 287e502815bf133f64afbf47211b11364a0a322f Mon Sep 17 00:00:00 2001 From: Mark Wielaard Date: Mon, 11 Nov 2019 00:15:55 +0100 Subject: 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 --- libdw/ChangeLog | 9 +++++++++ libdw/dwarf_getabbrev.c | 10 +++++++++- libdw/libdwP.h | 13 +++++++++++++ libdw/libdw_alloc.c | 12 ++++++++++++ 4 files changed, 43 insertions(+), 1 deletion(-) 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 + + * 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 * 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) { -- cgit v1.2.3