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

Reply via email to