Concurrent calls to dwarf_cfi_addrframe are unsynchronized and can
result in a CIE's initial state being initialized multiple times across
threads or a thread reading a partially initialized initial state.

Fix this by checking cie->initial_state atomically and if it isn't
initialized, acquire cache->lock before performing the initialization.

Signed-off-by: Aaron Merey <[email protected]>
---
 libdw/cfi.c | 21 +++++++++++++++++----
 1 file changed, 17 insertions(+), 4 deletions(-)

diff --git a/libdw/cfi.c b/libdw/cfi.c
index a9607246..4710b474 100644
--- a/libdw/cfi.c
+++ b/libdw/cfi.c
@@ -456,9 +456,14 @@ cie_cache_initial_state (Dwarf_CFI *cache, struct 
dwarf_cie *cie)
 {
   int result = DWARF_E_NOERROR;
 
-  if (likely (cie->initial_state != NULL))
+  if (likely (atomic_load_acquire (&cie->initial_state) != NULL))
     return result;
 
+  mutex_lock (cache->lock);
+
+  if (cie->initial_state != NULL)
+    goto out;
+
   /* This CIE has not been used before.  Play out its initial
      instructions and cache the initial state that results.
      First we'll let the backend fill in the default initial
@@ -477,11 +482,17 @@ cie_cache_initial_state (Dwarf_CFI *cache, struct 
dwarf_cie *cie)
   /* Fetch the ABI's default CFI program.  */
   if (likely (cache->ebl != (void *) -1l)
       && unlikely (ebl_abi_cfi (cache->ebl, &abi_info) < 0))
-    return DWARF_E_UNKNOWN_ERROR;
+    {
+      result = DWARF_E_UNKNOWN_ERROR;
+      goto out;
+    }
 
   Dwarf_Frame *cie_fs = calloc (1, sizeof (Dwarf_Frame));
   if (unlikely (cie_fs == NULL))
-    return DWARF_E_NOMEM;
+    {
+      result = DWARF_E_NOMEM;
+      goto out;
+    }
 
   /* If the default state of any register is not "undefined"
      (i.e. call-clobbered), then the backend supplies instructions
@@ -513,9 +524,11 @@ cie_cache_initial_state (Dwarf_CFI *cache, struct 
dwarf_cie *cie)
       /* Now we have the initial state of things that all
         FDEs using this CIE will start from.  */
       cie_fs->cache = cache;
-      cie->initial_state = cie_fs;
+      atomic_store_release (&cie->initial_state, cie_fs);
     }
 
+out:
+  mutex_unlock (cache->lock);
   return result;
 }
 
-- 
2.56.0

Reply via email to