Hi Aaron,

On Thu, May 29, 2025 at 05:44:32PM -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 might cause an unnecessary amount of memory to be allocated.
> 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.
> 
> 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>
> 
> v3:
> Implement _nolock functions as inline functions and move to eu-search.h.
> Simplify locking in is_constant_offset.
> In __libdw_findcu use eu_tfind instead of eu_tfind_nolock.

Thanks, this addresses all comments I had.

I'll see if my suggestion to make __libdw_fde_by_offset static makes
sense as a followup patch.

Thanks,

Mark

Reply via email to