__libdw_findabbrev tries to prevent a time-of-check to time-of-use race condition by double-checking whether the target abbrev entry has been read into cu->abbrev_hash. This double-check is conditional on all abbrev entries having already been read (indicated by cu->last_abbrev_offset == -1).
This conditional double-check causes a race condition. This can happen when the current thread's first Dwarf_Abbrev_Hash_find call returns NULL and, before the current thread can acquire the abbrev_lock mutex, another thread reads the target entry into cu->abbrev_hash but does not end up reading all entries. In this case, the current thread skips double-checking cu->abbrev_hash because cu->last_abbrev_offset != -1. It then attempts to read any remaining entries but fails to find the target entry because cu->last_abbrev_offset has already advanced past it. __libdw_findabbrev then returns DWARF_END_ABBREV despite the presence of a valid entry. Fix this by unconditionally double-checking cu->abbrev_hash after acquiring abbrev_lock. Signed-off-by: Aaron Merey <[email protected]> --- libdw/dwarf_tag.c | 54 +++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/libdw/dwarf_tag.c b/libdw/dwarf_tag.c index 9dfb653e..61e03264 100644 --- a/libdw/dwarf_tag.c +++ b/libdw/dwarf_tag.c @@ -51,33 +51,33 @@ __libdw_findabbrev (struct Dwarf_CU *cu, unsigned int code) 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; - } + was acquired. */ + 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. */ + 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); } -- 2.54.0
