Hi Aaron,

On Mon, May 19, 2025 at 03:10:30PM -0400, Aaron Merey wrote:
> eu_tfind is used to facilitate lazy loading throughout libdw.
> If a result is not found via eu_tfind, work is done to load
> the result and cache it in an eu_search_tree.
> 
> Some calls to eu_tfind allow for TOCTOU bugs.  Multiple threads
> might race to call eu_tfind on some result that hasn't yet been
> cached.  Multiple threads may then attempt to load the result
> which may cause an unnecessary amount of memory may be allocated.
        ^                                         ^
One of these mays seems redundant.

> Additionally this memory may not get released when the associated
> libdw data structure is freed.
> 
> Fix this by adding additional locking to ensure that only one
> thread performs lazy loading.
> 
> One approach used in this patch is to preserve calls to eu_tfind
> without additional locking, but when the result isn't found then
> a lock is then used to synchronize access to the lazy loading code.
> An extra eu_tfind call has been added at the start of these critical
> section to synchronize verification that lazy loading should proceed.
> 
> Another approach used is to simply synchronize entire calls to
> functions where lazy loading via eu_tfind might occur (__libdw_find_fde
> and __libdw_intern_expression).  In this case, new _nolock variants of
> the eu_t* functions are used to avoid unnecessary double locking.

Nice.

> lib/
>       * eu-search.c: Add eu_tsearch_nolock, eu_tfind_nolock and
>         eu_tdelete_nolock functions.
>       * eu-search.h: Ditto.
> 
> libdw/
>       * cfi.h (struct Dwarf_CFI_s): Declare new mutex.
>       * dwarf_begin_elf.c (valid_p): Initialize all locks for fake CUs.
>       * dwarf_cfi_addrframe.c (dwarf_cfi_addrframe): Place lock around
>       __libdw_find_fde.
>       * dwarf_end.c (cu_free): Deallocate all locks unconditionally,
>       whether or not the CU is fake.
>       * dwarf_frame_cfa.c (dwarf_frame_cfa): Place lock around
>       __libdw_intern_expression.
>       * dwarf_frame_register.c (dwarf_frame_register): Ditto.
>       * dwarf_getcfi.c (dwarf_getcfi): Initialize cfi lock.
>       * dwarf_getlocation.c (is_constant_offset): Synchronize access
>       to lazy loading section.
>       (getlocation): Place lock around __libdw_intern_expression.
>       * dwarf_getmacros.c (cache_op_table): Synchronize access to lazy
>       loading section.
>       * frame-cache.c (__libdw_destroy_frame_cache): Free Dwarf_CFI
>       mutex.
>       * libdwP.h (struct Dwarf): Update macro_lock comment.
>       (struct Dwarf_CU): Declare new mutex.
>       libdw_findcu.c (__libdw_intern_next_unit): Initialize
>       intern_lock.
>       (__libdw_findcu): Adjust locking so that the first eu_tfind
>       can be done without extra lock overhead.
> 
> Signed-off-by: Aaron Merey <ame...@redhat.com>
> 
> ---
> v2:
> 
> _nolock variants of eu_t* functions have been added to avoid double
> locking.  _nolock functions are now used where appropriate.
> 
> Clarified that __libdw_intern_expression should be called with a lock
> held by the owner of the search_tree passed to this function (typically
> a Dwarf_CFI_s or Dwarf_CU).
> 
>  lib/eu-search.c              | 18 ++++++++++
>  lib/eu-search.h              | 12 +++++++
>  libdw/cfi.h                  |  4 +++
>  libdw/dwarf_begin_elf.c      | 15 ++++++++
>  libdw/dwarf_cfi_addrframe.c  |  3 ++
>  libdw/dwarf_end.c            | 10 +++---
>  libdw/dwarf_frame_cfa.c      |  2 ++
>  libdw/dwarf_frame_register.c | 16 +++++----
>  libdw/dwarf_getcfi.c         |  1 +
>  libdw/dwarf_getlocation.c    | 69 ++++++++++++++++++++++--------------
>  libdw/dwarf_getmacros.c      | 20 +++++++++--
>  libdw/fde.c                  |  6 ++--
>  libdw/frame-cache.c          |  1 +
>  libdw/libdwP.h               | 11 ++++--
>  libdw/libdw_findcu.c         |  9 +++--
>  15 files changed, 150 insertions(+), 47 deletions(-)
> 
> diff --git a/lib/eu-search.c b/lib/eu-search.c
> index fc31fe87..a49d8dd3 100644
> --- a/lib/eu-search.c
> +++ b/lib/eu-search.c
> @@ -62,6 +62,24 @@ void *eu_tdelete (const void *key, search_tree *tree,
>    return ret;
>  }
>  
> +void *eu_tsearch_nolock (const void *key, search_tree *tree,
> +                      int (*compare)(const void *, const void *))
> +{
> +  return tsearch (key, &tree->root, compare);
> +}
> +
> +void *eu_tfind_nolock (const void *key, search_tree *tree,
> +                    int (*compare)(const void *, const void *))
> +{
> +  return tfind (key, &tree->root, compare);
> +}
> +
> +void *eu_tdelete_nolock (const void *key, search_tree *tree,
> +                      int (*compare)(const void *, const void *))
> +{
> +  return tdelete (key, &tree->root, compare);
> +}
> +
>  void eu_tdestroy (search_tree *tree, void (*free_node)(void *))
>  {
>    rwlock_wrlock (tree->lock);
>
> diff --git a/lib/eu-search.h b/lib/eu-search.h
> index 67b54c18..841a7f64 100644
> --- a/lib/eu-search.h
> +++ b/lib/eu-search.h
> @@ -52,6 +52,18 @@ extern void *eu_tfind (const void *key, search_tree *tree,
>  extern void *eu_tdelete (const void *key, search_tree *tree,
>                        int (*compare)(const void *, const void *));
>  
> +/* Search TREE for KEY and add KEY if not found.  No locking is performed.  
> */
> +extern void *eu_tsearch_nolock (const void *key, search_tree *tree,
> +                             int (*compare)(const void *, const void *));
> +
> +/* Search TREE for KEY.  No locking is performed.  */
> +extern void *eu_tfind_nolock (const void *key, search_tree *tree,
> +                           int (*compare)(const void *, const void *));
> +
> +/* Delete key from TREE.  No locking is performed.  */
> +extern void *eu_tdelete_nolock (const void *key, search_tree *tree,
> +                             int (*compare)(const void *, const void *));
> +
>  /* Free all nodes from TREE.  */
>  void eu_tdestroy (search_tree *tree, void (*free_node)(void *));

Nitpick and probably premature optimization.  But in theory you can
define such simple wrapper functions as static inline just in the
header file.
  
> diff --git a/libdw/cfi.h b/libdw/cfi.h
> index d0134243..f0296de7 100644
> --- a/libdw/cfi.h
> +++ b/libdw/cfi.h
> @@ -98,6 +98,10 @@ struct Dwarf_CFI_s
>    /* Search tree for parsed DWARF expressions, indexed by raw pointer.  */
>    search_tree expr_tree;
>  
> +  /* Should be held when calling __libdw_find_fde, __libdw_fde_by_offset and
> +     when __libdw_intern_expression is called with Dwarf_CFI members.  */
> +  mutex_define(, lock);
> +
>    /* Backend hook.  */
>    struct ebl *ebl;

Nicely documented. But see my comment below about __libdw_fde_by_offset.

> diff --git a/libdw/dwarf_begin_elf.c b/libdw/dwarf_begin_elf.c
> index 58a309e9..63e2805d 100644
> --- a/libdw/dwarf_begin_elf.c
> +++ b/libdw/dwarf_begin_elf.c
> @@ -359,6 +359,11 @@ valid_p (Dwarf *result)
>         result->fake_loc_cu->version = 4;
>         result->fake_loc_cu->split = NULL;
>         eu_search_tree_init (&result->fake_loc_cu->locs_tree);
> +       rwlock_init (result->fake_loc_cu->abbrev_lock);
> +       rwlock_init (result->fake_loc_cu->split_lock);
> +       mutex_init (result->fake_loc_cu->src_lock);
> +       mutex_init (result->fake_loc_cu->str_off_base_lock);
> +       mutex_init (result->fake_loc_cu->intern_lock);
>       }
>      }
>  
> @@ -387,6 +392,11 @@ valid_p (Dwarf *result)
>         result->fake_loclists_cu->version = 5;
>         result->fake_loclists_cu->split = NULL;
>         eu_search_tree_init (&result->fake_loclists_cu->locs_tree);
> +       rwlock_init (result->fake_loclists_cu->abbrev_lock);
> +       rwlock_init (result->fake_loclists_cu->split_lock);
> +       mutex_init (result->fake_loclists_cu->src_lock);
> +       mutex_init (result->fake_loclists_cu->str_off_base_lock);
> +       mutex_init (result->fake_loclists_cu->intern_lock);
>       }
>      }
>  
> @@ -420,6 +430,11 @@ valid_p (Dwarf *result)
>         result->fake_addr_cu->version = 5;
>         result->fake_addr_cu->split = NULL;
>         eu_search_tree_init (&result->fake_addr_cu->locs_tree);
> +       rwlock_init (result->fake_addr_cu->abbrev_lock);
> +       rwlock_init (result->fake_addr_cu->split_lock);
> +       mutex_init (result->fake_addr_cu->src_lock);
> +       mutex_init (result->fake_addr_cu->str_off_base_lock);
> +       mutex_init (result->fake_addr_cu->intern_lock);
>       }
>      }

Ack. Also init locks for the fake CUs.
  
> diff --git a/libdw/dwarf_cfi_addrframe.c b/libdw/dwarf_cfi_addrframe.c
> index 44240279..f391f9f9 100644
> --- a/libdw/dwarf_cfi_addrframe.c
> +++ b/libdw/dwarf_cfi_addrframe.c
> @@ -39,7 +39,10 @@ dwarf_cfi_addrframe (Dwarf_CFI *cache, Dwarf_Addr address, 
> Dwarf_Frame **frame)
>    if (cache == NULL)
>      return -1;
>  
> +  mutex_lock (cache->lock);
>    struct dwarf_fde *fde = __libdw_find_fde (cache, address);
> +  mutex_unlock (cache->lock);
> +
>    if (fde == NULL)
>      return -1;

OK, lock around __libdw_find_fde.

> diff --git a/libdw/dwarf_end.c b/libdw/dwarf_end.c
> index 1628e448..979b1168 100644
> --- a/libdw/dwarf_end.c
> +++ b/libdw/dwarf_end.c
> @@ -61,17 +61,19 @@ static void
>  cu_free (void *arg)
>  {
>    struct Dwarf_CU *p = (struct Dwarf_CU *) arg;
> +
>    eu_search_tree_fini (&p->locs_tree, noop_free);
> +  rwlock_fini (p->abbrev_lock);
> +  rwlock_fini (p->split_lock);
> +  mutex_fini (p->src_lock);
> +  mutex_fini (p->str_off_base_lock);
> +  mutex_fini (p->intern_lock);
>  
>    /* Only free the CU internals if its not a fake CU.  */
>    if (p != p->dbg->fake_loc_cu && p != p->dbg->fake_loclists_cu
>       && p != p->dbg->fake_addr_cu)
>      {
>        Dwarf_Abbrev_Hash_free (&p->abbrev_hash);
> -      rwlock_fini (p->abbrev_lock);
> -      rwlock_fini (p->split_lock);
> -      mutex_fini (p->src_lock);
> -      mutex_fini (p->str_off_base_lock);
>  
>        /* Free split dwarf one way (from skeleton to split).  */
>        if (p->unit_type == DW_UT_skeleton

Ack, fake cu locking fixed.

> diff --git a/libdw/dwarf_frame_cfa.c b/libdw/dwarf_frame_cfa.c
> index 07f998cd..c18ee499 100644
> --- a/libdw/dwarf_frame_cfa.c
> +++ b/libdw/dwarf_frame_cfa.c
> @@ -57,11 +57,13 @@ dwarf_frame_cfa (Dwarf_Frame *fs, Dwarf_Op **ops, size_t 
> *nops)
>  
>      case cfa_expr:
>        /* Parse the expression into internal form.  */
> +      mutex_lock (fs->cache->lock);
>        result = __libdw_intern_expression
>       (NULL, fs->cache->other_byte_order,
>        fs->cache->e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8, 4,
>        &fs->cache->expr_tree, &fs->cfa_data.expr, false, false,
>        ops, nops, IDX_debug_frame);
> +      mutex_unlock (fs->cache->lock);
>        break;
>  
>      case cfa_invalid:

Ack, locking around __libdw_intern_expression.

> diff --git a/libdw/dwarf_frame_register.c b/libdw/dwarf_frame_register.c
> index a6b7c4c1..dbd7f1b2 100644
> --- a/libdw/dwarf_frame_register.c
> +++ b/libdw/dwarf_frame_register.c
> @@ -109,12 +109,16 @@ dwarf_frame_register (Dwarf_Frame *fs, int regno, 
> Dwarf_Op ops_mem[3],
>       block.data = (void *) p;
>  
>       /* Parse the expression into internal form.  */
> -     if (__libdw_intern_expression (NULL,
> -                                    fs->cache->other_byte_order,
> -                                    address_size, 4,
> -                                    &fs->cache->expr_tree, &block,
> -                                    true, reg->rule == reg_val_expression,
> -                                    ops, nops, IDX_debug_frame) < 0)
> +     mutex_lock (fs->cache->lock);
> +     int res = __libdw_intern_expression (NULL,
> +                                          fs->cache->other_byte_order,
> +                                          address_size, 4,
> +                                          &fs->cache->expr_tree, &block,
> +                                          true, reg->rule == 
> reg_val_expression,
> +                                          ops, nops, IDX_debug_frame);
> +     mutex_unlock (fs->cache->lock);
> +
> +     if (res < 0)
>         return -1;
>       break;
>        }

Likewise.

> diff --git a/libdw/dwarf_getcfi.c b/libdw/dwarf_getcfi.c
> index a4497152..893e3c74 100644
> --- a/libdw/dwarf_getcfi.c
> +++ b/libdw/dwarf_getcfi.c
> @@ -70,6 +70,7 @@ dwarf_getcfi (Dwarf *dbg)
>        eu_search_tree_init (&cfi->cie_tree);
>        eu_search_tree_init (&cfi->fde_tree);
>        eu_search_tree_init (&cfi->expr_tree);
> +      mutex_init (cfi->lock);
>  
>        cfi->ebl = NULL;

Ack. Init lock.

> diff --git a/libdw/dwarf_getlocation.c b/libdw/dwarf_getlocation.c
> index ad1d46ca..2a2fd665 100644
> --- a/libdw/dwarf_getlocation.c
> +++ b/libdw/dwarf_getlocation.c
> @@ -154,7 +154,7 @@ store_implicit_value (Dwarf *dbg, search_tree *cache, 
> Dwarf_Op *op)
>    block->addr = op;
>    block->data = (unsigned char *) data;
>    block->length = op->number;
> -  if (unlikely (eu_tsearch (block, cache, loc_compare) == NULL))
> +  if (unlikely (eu_tsearch_nolock (block, cache, loc_compare) == NULL))
>      return 1;
>    return 0;
>  }

store_implicit_value called from __libdw_intern_expression which
should only be called locked. So nolock usage is ok here.

> @@ -216,27 +216,38 @@ is_constant_offset (Dwarf_Attribute *attr,
>  
>    if (found == NULL)
>      {

This found is the result of an eu_tfind. Why doesn't that one need to
be under the lock? We do another lock and eu_find_nolock below, so I
think it is correct. But wouldn't it be more efficient to have just
one big lock and one tfind?

> -      Dwarf_Word offset;
> -      if (INTUSE(dwarf_formudata) (attr, &offset) != 0)
> -     return -1;
> +      mutex_lock (attr->cu->intern_lock);
> +
> +      found = eu_tfind_nolock (&fake, &attr->cu->locs_tree, loc_compare);
> +      if (found == NULL)
> +     {
> +       Dwarf_Word offset;
> +       if (INTUSE(dwarf_formudata) (attr, &offset) != 0)
> +         {
> +           mutex_unlock (attr->cu->intern_lock);
> +           return -1;
> +         }
>  
> -      Dwarf_Op *result = libdw_alloc (attr->cu->dbg,
> -                                   Dwarf_Op, sizeof (Dwarf_Op), 1);
> +       Dwarf_Op *result = libdw_alloc (attr->cu->dbg,
> +                                       Dwarf_Op, sizeof (Dwarf_Op), 1);
>  
> -      result->atom = DW_OP_plus_uconst;
> -      result->number = offset;
> -      result->number2 = 0;
> -      result->offset = 0;
> +       result->atom = DW_OP_plus_uconst;
> +       result->number = offset;
> +       result->number2 = 0;
> +       result->offset = 0;
>  
> -      /* Insert a record in the search tree so we can find it again later.  
> */
> -      struct loc_s *newp = libdw_alloc (attr->cu->dbg,
> -                                     struct loc_s, sizeof (struct loc_s),
> -                                     1);
> -      newp->addr = attr->valp;
> -      newp->loc = result;
> -      newp->nloc = 1;
> +       /* Insert a record in the search tree so we can find it again later.  
> */
> +       struct loc_s *newp = libdw_alloc (attr->cu->dbg,
> +                                         struct loc_s, sizeof (struct loc_s),
> +                                         1);
> +       newp->addr = attr->valp;
> +       newp->loc = result;
> +       newp->nloc = 1;
> +
> +       found = eu_tsearch_nolock (newp, &attr->cu->locs_tree, loc_compare);
> +     }
>  
> -      found = eu_tsearch (newp, &attr->cu->locs_tree, loc_compare);
> +      mutex_unlock (attr->cu->intern_lock);
>      }
>  
>    assert ((*found)->nloc == 1);

And unlock, so all calls to nolock are ok.

> @@ -267,7 +278,7 @@ __libdw_intern_expression (Dwarf *dbg, bool 
> other_byte_order,
>  
>    /* Check whether we already looked at this list.  */
>    struct loc_s fake = { .addr = block->data };
> -  struct loc_s **found = eu_tfind (&fake, cache, loc_compare);
> +  struct loc_s **found = eu_tfind_nolock (&fake, cache, loc_compare);
>    if (found != NULL)
>      {
>        /* We already saw it.  */
> @@ -656,7 +667,7 @@ __libdw_intern_expression (Dwarf *dbg, bool 
> other_byte_order,
>    newp->addr = block->data;
>    newp->loc = result;
>    newp->nloc = *listlen;
> -  eu_tsearch (newp, cache, loc_compare);
> +  eu_tsearch_nolock (newp, cache, loc_compare);
>  
>    /* We did it.  */
>    return 0;

OK, all calls to __libdw_intern_expression hold locks associated with
the cache, so we can use the nolock variants here.

> @@ -674,13 +685,17 @@ getlocation (struct Dwarf_CU *cu, const Dwarf_Block 
> *block,
>        return 0;
>      }
>  
> -  return __libdw_intern_expression (cu->dbg, cu->dbg->other_byte_order,
> -                                 cu->address_size, (cu->version == 2
> -                                                    ? cu->address_size
> -                                                    : cu->offset_size),
> -                                 &cu->locs_tree, block,
> -                                 false, false,
> -                                 llbuf, listlen, sec_index);
> +  mutex_lock (cu->intern_lock);
> +  int res = __libdw_intern_expression (cu->dbg, cu->dbg->other_byte_order,
> +                                    cu->address_size, (cu->version == 2
> +                                                       ? cu->address_size
> +                                                       : cu->offset_size),
> +                                    &cu->locs_tree, block,
> +                                    false, false,
> +                                    llbuf, listlen, sec_index);
> +  mutex_unlock (cu->intern_lock);
> +
> +  return res;
>  }

OK cu intern_lock for __libdw_intern_expression as a whole.

>  int
> diff --git a/libdw/dwarf_getmacros.c b/libdw/dwarf_getmacros.c
> index f1c831fa..d7ed7b58 100644
> --- a/libdw/dwarf_getmacros.c
> +++ b/libdw/dwarf_getmacros.c
> @@ -322,15 +322,29 @@ cache_op_table (Dwarf *dbg, int sec_index, Dwarf_Off 
> macoff,
>    if (found != NULL)
>      return *found;
>  
> +  mutex_lock (dbg->macro_lock);
> +
> +  found = eu_tfind_nolock (&fake, &dbg->macro_ops_tree, macro_op_compare);
> +  if (found != NULL)
> +    {
> +      mutex_unlock (dbg->macro_lock);
> +      return *found;
> +    }
> +

Ack, lock and nolock.

>    Dwarf_Macro_Op_Table *table = sec_index == IDX_debug_macro
>      ? get_table_for_offset (dbg, macoff, startp, endp, cudie)
>      : get_macinfo_table (dbg, macoff, cudie);
>  
>    if (table == NULL)
> -    return NULL;
> +    {
> +      mutex_unlock (dbg->macro_lock);
> +      return NULL;
> +    }
> +
> +  Dwarf_Macro_Op_Table **ret = eu_tsearch_nolock (table, 
> &dbg->macro_ops_tree,
> +                                               macro_op_compare);
> +  mutex_unlock (dbg->macro_lock);
>  
> -  Dwarf_Macro_Op_Table **ret = eu_tsearch (table, &dbg->macro_ops_tree,
> -                                     macro_op_compare);
>    if (unlikely (ret == NULL))
>      {
>        __libdw_seterrno (DWARF_E_NOMEM);

Ack. Likewise.

> diff --git a/libdw/fde.c b/libdw/fde.c
> index a5167805..7f36c4a9 100644
> --- a/libdw/fde.c
> +++ b/libdw/fde.c
> @@ -122,7 +122,8 @@ intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry)
>      fde->instructions += cie->fde_augmentation_data_size;
>  
>    /* Add the new entry to the search tree.  */
> -  struct dwarf_fde **tres = eu_tsearch (fde, &cache->fde_tree, &compare_fde);
> +  struct dwarf_fde **tres = eu_tsearch_nolock (fde, &cache->fde_tree,
> +                                            &compare_fde);
>    if (tres == NULL)
>      {
>        free (fde);

intern_fde is called from __libdw_fde_by_offset and __libdw_find_fde.
__libdw_fde_by_offset is only called from __libdw_find_fde. And
__libdw_find_fde should always be locked. So using eu_tsearch_nolock
here is fine.

As a followup patch we could make __libdw_fde_by_offset static in
libdw/fde.c and remove the extern definition in libdw/cfi.h.

> @@ -252,7 +253,8 @@ __libdw_find_fde (Dwarf_CFI *cache, Dwarf_Addr address)
>    /* Look for a cached FDE covering this address.  */
>  
>    const struct dwarf_fde fde_key = { .start = address, .end = 0 };
> -  struct dwarf_fde **found = eu_tfind (&fde_key, &cache->fde_tree, 
> &compare_fde);
> +  struct dwarf_fde **found = eu_tfind_nolock (&fde_key, &cache->fde_tree,
> +                                           &compare_fde);
>    if (found != NULL)
>      return *found;
>  

Right. __libdw_find_fde should only be called locked. So the nolock
call is correct here.

> diff --git a/libdw/frame-cache.c b/libdw/frame-cache.c
> index 6c89858a..6f6f777e 100644
> --- a/libdw/frame-cache.c
> +++ b/libdw/frame-cache.c
> @@ -64,6 +64,7 @@ __libdw_destroy_frame_cache (Dwarf_CFI *cache)
>    eu_search_tree_fini (&cache->fde_tree, free_fde);
>    eu_search_tree_fini (&cache->cie_tree, free_cie);
>    eu_search_tree_fini (&cache->expr_tree, free_expr);
> +  mutex_fini (cache->lock);
>  
>    if (cache->ebl != NULL && cache->ebl != (void *) -1l)
>      ebl_closebackend (cache->ebl);

Ack.

> diff --git a/libdw/libdwP.h b/libdw/libdwP.h
> index de80cd4e..25d53d31 100644
> --- a/libdw/libdwP.h
> +++ b/libdw/libdwP.h
> @@ -269,7 +269,7 @@ struct Dwarf
>       __libdw_intern_next_unit.  */
>    mutex_define(, dwarf_lock);
>  
> -  /* Synchronize access to dwarf_macro_getsrcfiles.  */
> +  /* Synchronize access to dwarf_macro_getsrcfiles and cache_op_table.  */
>    mutex_define(, macro_lock);
>  
>    /* Internal memory handling.  This is basically a simplified thread-local
> @@ -471,6 +471,10 @@ struct Dwarf_CU
>       Covers __libdw_str_offsets_base_off.  */
>    mutex_define(, str_off_base_lock);
>  
> +  /* Synchronize access to is_constant_offset.  Should also be held
> +     when calling __libdw_intern_expression with Dwarf_CU members.  */
> +  mutex_define(, intern_lock);
> +
>    /* Memory boundaries of this CU.  */
>    void *startp;
>    void *endp;

Ack.

> @@ -949,8 +953,9 @@ extern int __libdw_visit_scopes (unsigned int depth,
>                                void *arg)
>    __nonnull_attribute__ (2, 4) internal_function;
>  
> -/* Parse a DWARF Dwarf_Block into an array of Dwarf_Op's,
> -   and cache the result (via tsearch).  */
> +/* Parse a DWARF Dwarf_Block into an array of Dwarf_Op's, and cache the
> +   result (via tsearch).  The owner of CACHE (typically a Dwarf_CU or
> +   Dwarf_CFI_s) must hold a lock when calling this function.  */
>  extern int __libdw_intern_expression (Dwarf *dbg,
>                                     bool other_byte_order,
>                                     unsigned int address_size,

Nice docks.

> diff --git a/libdw/libdw_findcu.c b/libdw/libdw_findcu.c
> index 0e4dcc37..21c8ed0e 100644
> --- a/libdw/libdw_findcu.c
> +++ b/libdw/libdw_findcu.c
> @@ -181,6 +181,7 @@ __libdw_intern_next_unit (Dwarf *dbg, bool debug_types)
>    rwlock_init (newp->split_lock);
>    mutex_init (newp->src_lock);
>    mutex_init (newp->str_off_base_lock);
> +  mutex_init (newp->intern_lock);
>  
>    /* v4 debug type units have version == 4 and unit_type == DW_UT_type.  */
>    if (debug_types)

Ack.

> @@ -240,8 +241,6 @@ struct Dwarf_CU *
>  internal_function
>  __libdw_findcu (Dwarf *dbg, Dwarf_Off start, bool v4_debug_types)
>  {
> -  mutex_lock (dbg->dwarf_lock);
> -
>    search_tree *tree = v4_debug_types ? &dbg->tu_tree : &dbg->cu_tree;
>    Dwarf_Off *next_offset
>      = v4_debug_types ? &dbg->next_tu_offset : &dbg->next_cu_offset;
> @@ -250,6 +249,12 @@ __libdw_findcu (Dwarf *dbg, Dwarf_Off start, bool 
> v4_debug_types)
>    struct Dwarf_CU fake = { .start = start, .end = 0 };
>    struct Dwarf_CU **found = eu_tfind (&fake, tree, findcu_cb);
>    struct Dwarf_CU *result = NULL;
> +  if (found != NULL)
> +    return *found;
> +
> +  mutex_lock (dbg->dwarf_lock);
> +
> +  found = eu_tfind_nolock (&fake, tree, findcu_cb);
>    if (found != NULL)
>      {
>        mutex_unlock (dbg->dwarf_lock);

OK, so the dwarf_lock has to be held to call __libdw_intern_next_unit
below. But why is it correct to call eu_tfind_nolock here without
holding the tree lock?

Thanks,

Mark

Reply via email to