__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

Reply via email to