Hi Aaron,

On Sun, Jun 21, 2026 at 11:44:42PM -0400, Aaron Merey wrote:
> __libdw_findabbrev tries to prevent a time-of-check to time-of-use race
> condition by double-checking whether the target abbrev entry has been read
> into cu->abbrev_hash.  This double-check is conditional on all abbrev
> entries having already been read (indicated by cu->last_abbrev_offset == -1).
> 
> This conditional double-check causes a race condition.  This can happen
> when the current thread's first Dwarf_Abbrev_Hash_find call returns NULL
> and, before the current thread can acquire the abbrev_lock mutex, another
> thread reads the target entry into cu->abbrev_hash but does not end up
> reading all entries.
> 
> In this case, the current thread skips double-checking cu->abbrev_hash
> because cu->last_abbrev_offset != -1.  It then attempts to read any
> remaining entries but fails to find the target entry because
> cu->last_abbrev_offset has already advanced past it.  __libdw_findabbrev
> then returns DWARF_END_ABBREV despite the presence of a valid entry.

Urgh, subtle. Had to reread your description and the code a couple of
times to fully appreciate the race.

> Fix this by unconditionally double-checking cu->abbrev_hash after
> acquiring abbrev_lock.

Looks correct.
The diff looks big, but with -w -b it is just:

diff --git a/libdw/dwarf_tag.c b/libdw/dwarf_tag.c
index 9dfb653edf90..61e03264fb89 100644
--- a/libdw/dwarf_tag.c
+++ b/libdw/dwarf_tag.c
@@ -51,10 +51,10 @@ __libdw_findabbrev (struct Dwarf_CU *cu, unsigned int code)
       mutex_lock (cu->abbrev_lock);
 
       /* Check once more in case entry was added before abbrev_lock
-        was aquired.  */
-      if (cu->last_abbrev_offset == (size_t) -1l)
+        was acquired.  */
       abb = Dwarf_Abbrev_Hash_find (&cu->abbrev_hash, code);
 
+      if (abb == NULL)
        while (cu->last_abbrev_offset != (size_t) -1l)
          {
            size_t length;

Thanks,

Mark

Reply via email to