Acquire abbrev_lock before reading cu->last_abbrev_offset in dwarf_tag. Also double check for abbrev hash table entry once lock is held to avoid multiple threads attempting to populate the abbrev hash table.
Signed-off-by: Aaron Merey <[email protected]> --- v2: Refactor based on Mark's suggestion: https://patchwork.sourceware.org/project/elfutils/patch/[email protected]/#224420 libdw/dwarf_tag.c | 58 +++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/libdw/dwarf_tag.c b/libdw/dwarf_tag.c index 7daa4813..9dfb653e 100644 --- a/libdw/dwarf_tag.c +++ b/libdw/dwarf_tag.c @@ -47,30 +47,40 @@ __libdw_findabbrev (struct Dwarf_CU *cu, unsigned int code) /* See whether the entry is already in the hash table. */ abb = Dwarf_Abbrev_Hash_find (&cu->abbrev_hash, code); if (abb == NULL) - while (cu->last_abbrev_offset != (size_t) -1l) - { - size_t length; - - /* Find the next entry. It gets automatically added to the - hash table. */ - mutex_lock (cu->abbrev_lock); - abb = __libdw_getabbrev (cu->dbg, cu, cu->last_abbrev_offset, &length); - - if (abb == NULL || abb == DWARF_END_ABBREV) - { - /* Make sure we do not try to search for it again. */ - cu->last_abbrev_offset = (size_t) -1l; - mutex_unlock (cu->abbrev_lock); - return DWARF_END_ABBREV; - } - - cu->last_abbrev_offset += length; - mutex_unlock (cu->abbrev_lock); - - /* Is this the code we are looking for? */ - if (abb->code == code) - break; - } + { + mutex_lock (cu->abbrev_lock); + + /* Check once more in case entry was added before abbrev_lock + was aquired. */ + if (cu->last_abbrev_offset == (size_t) -1l) + abb = Dwarf_Abbrev_Hash_find (&cu->abbrev_hash, code); + + while (cu->last_abbrev_offset != (size_t) -1l) + { + size_t length; + + /* Find the next entry. It gets automatically added to the + hash table. */ + abb = __libdw_getabbrev (cu->dbg, cu, cu->last_abbrev_offset, + &length); + + if (abb == NULL || abb == DWARF_END_ABBREV) + { + /* Make sure we do not try to search for it again. */ + cu->last_abbrev_offset = (size_t) -1l; + mutex_unlock (cu->abbrev_lock); + return DWARF_END_ABBREV; + } + + cu->last_abbrev_offset += length; + + /* Is this the code we are looking for? */ + if (abb->code == code) + break; + } + + mutex_unlock (cu->abbrev_lock); + } /* This is our second (or third, etc.) call to __libdw_findabbrev and the code is invalid. */ -- 2.53.0
