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]>
---
 libdw/dwarf_tag.c | 48 ++++++++++++++++++++++++++++-------------------
 1 file changed, 29 insertions(+), 19 deletions(-)

diff --git a/libdw/dwarf_tag.c b/libdw/dwarf_tag.c
index 7daa4813..69e3bdc2 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;
+    {
+      mutex_lock (cu->abbrev_lock);
 
-       /* 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);
+      /* Check once more in case entry was added before abbrev_lock
+        was aquired.  */
+      abb = Dwarf_Abbrev_Hash_find (&cu->abbrev_hash, code);
 
-       if (abb == NULL || abb == DWARF_END_ABBREV)
+      if (abb == NULL)
+       while (cu->last_abbrev_offset != (size_t) -1l)
          {
-           /* 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;
+           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;
          }
 
-       cu->last_abbrev_offset += length;
-       mutex_unlock (cu->abbrev_lock);
-
-       /* 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.52.0

Reply via email to