[PATCH v3] libdw: Fix eu_search_tree TOCTOU bugs

2025-05-29 Thread Aaron Merey
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 

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.
---
 lib/eu-search.h  | 24 
 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| 35 ++-
 libdw/dwarf_getmacros.c  | 20 +---
 libdw/fde.c  |  6 --
 libdw/frame-cache.c  |  1 +
 libdw/libdwP.h   | 11 ---
 libdw/libdw_findcu.c |  9 +++--
 14 files changed, 124 insertions(+), 33 deletions(-)

diff --git a/lib/eu-search.h b/lib/eu-search.h
index 67b54c18..4299e115 100644
--- a/lib/eu-search.h
+++ b/lib/eu-search.h
@@ -52,6 +52,30 @@ 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.  */
+static inline void *
+eu_tsearch_nolock (const void *key, search_tree *tree,
+  int (*compare)(const void *, const void *))
+{
+  return tsearch (key, &tree->root, compare);
+}
+
+/* Search TREE for KEY.  No locking is performed.  */
+static inline void *
+eu_tfind_nolock (const void *key, search_tree *tree,
+int (*compare)(const void *, const void *))
+{
+  return tfind (key, &tree->root, compare);
+}
+
+/* Delete key from TREE.  No locking is performed.  */
+static inline void *
+eu_tdelete_nolock (const void *key, search_tree *tree,
+  int (*compare)(const void *, const void *))
+{
+  return tdelete (key, &tree->root, compare);
+}
+
 /* Free all nodes from TREE.  */
 void eu_tdestroy (search_tree *tree, void (*free_node)(void *));
 
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;
 
+  /* Sho

Re: [PATCH] backends/ppc_attrs.c: Add PPC long double tags

2025-05-29 Thread A. Wilcox
Hi Mark,

Unfortunately, the only reference I am aware of for GNU attributes is
the GCC compiler source itself; these constants are defined in
gcc/config/rs6000/rs6000.cc in rs6000_elf_file_end.

Best,
-Anna

On May 27, 2025, at 8:19 AM, Mark Wielaard  wrote:
> Hi,
> 
> On Tue, 2025-05-27 at 00:05 -0500, A. Wilcox wrote:
>> When an explicit type of long double is specified in the ELF
>> GNU_Power_ABI_FP attribute, elflint and friends were erroring out:
>> 
>>section [36] '.gnu.attributes': offset 15: unrecognized GNU_Power_ABI_FP 
>> attribute value 9
>> 
>> Add the different long double tags to fp_kinds so that these values
>> are correctly recognised and printed.
> 
> This is probably correct, but do you happen to have a reference where
> these GNU_Power_ABI_FP attribute are defined? Then we can add that to
> the source as comment so we can easily look them up in the future.
> 
> Cheers,
> 
> Mark
> 
>> Signed-off-by: A. Wilcox 
>> ---
>> backends/ppc_attrs.c | 12 
>> 1 file changed, 12 insertions(+)
>> 
>> diff --git a/backends/ppc_attrs.c b/backends/ppc_attrs.c
>> index 48d7129d..6b00bccd 100644
>> --- a/backends/ppc_attrs.c
>> +++ b/backends/ppc_attrs.c
>> @@ -52,6 +52,18 @@ ppc_check_object_attribute (Ebl *ebl __attribute__ 
>> ((unused)),
>>"Hard float",
>>"Soft float",
>>"Single-precision hard float",
>> +"Hard or soft float (IBM style long doubles)",
>> +"Hard float (IBM style long doubles)",
>> +"Soft float (IBM style long doubles)",
>> +"Single-precision hard float (IBM style long doubles)",
>> +"Hard or soft float (64-bit long doubles)",
>> +"Hard float (64-bit long doubles)",
>> +"Soft float (64-bit long doubles)",
>> +"Single-precision hard float (64-bit long doubles)",
>> +"Hard or soft float (IEEE 128-bit long doubles)",
>> +"Hard float (IEEE 128-bit long doubles)",
>> +"Soft float (IEEE 128-bit long doubles)",
>> +"Single-precision hard float (IEEE 128-bit long doubles)",
>>  };
>> if (value < sizeof fp_kinds / sizeof fp_kinds[0])
>>  *value_name = fp_kinds[value];