Make __libdwfl_debuginfod_find_* functions thread safe

Message ID 20251201171719.825918-1-amerey@redhat.com
State Superseded
Delegated to: Mark Wielaard
Headers
Series Make __libdwfl_debuginfod_find_* functions thread safe |

Commit Message

Aaron Merey Dec. 1, 2025, 5:17 p.m. UTC
  Individual debuginfod_client objects and their underlying CURLM handles
should not be used concurrently during calls to
__libdwfl_debuginfod_find_debuginfo and
__libdwfl_debuginfod_find_executable.

Add a mutex field to struct Dwfl and synchronize calls to
__libdwfl_debuginfod_find_*.

Signed-off-by: Aaron Merey <amerey@redhat.com>
---
 libdwfl/debuginfod-client.c | 9 +++++++--
 libdwfl/dwfl_begin.c        | 2 ++
 libdwfl/dwfl_end.c          | 1 +
 libdwfl/libdwflP.h          | 1 +
 4 files changed, 11 insertions(+), 2 deletions(-)
  

Comments

Frank Ch. Eigler Dec. 1, 2025, 9:44 p.m. UTC | #1
Hi -

> Individual debuginfod_client objects and their underlying CURLM handles
> should not be used concurrently [...]

Yeah, pity eh!  libdebuginfod is perfectly happy with concurrent
calls, but only on different debuginfod_client handles, rather than
the single shared one tracked by libdwfl.  It's unlikely to be worth
the effort to keep a pool of such handles to be shared among threads
... but maybe data could answer that question.

- FChE
  
Mark Wielaard Dec. 3, 2025, 4:19 p.m. UTC | #2
Hi Aaron,

On Mon, 2025-12-01 at 12:17 -0500, Aaron Merey wrote:
> Individual debuginfod_client objects and their underlying CURLM handles
> should not be used concurrently during calls to
> __libdwfl_debuginfod_find_debuginfo and
> __libdwfl_debuginfod_find_executable.
> 
> Add a mutex field to struct Dwfl and synchronize calls to
> __libdwfl_debuginfod_find_*.

So if I understand correctly there are two issues here.

The debuginfo_client is created and assigned to a Dwfl field lazily. So
you want to prevent concurrent calls to dwfl_get_debuginfod_client.

A debuginfo_client has CURLM handle which cannot be used by different
threads concurrently.

Since we have one unique debuginfo_client per dwfl you use one and the
same lock?

I believe this patch should work. But have you considered having a lock
inside debuginfod-client itself to protect access to the CURLM handles?
That would (maybe) result is slightly simpler code. And would help
other users of libdebuginfod that might use debuginfod_client handles
from multiple threads (gdb?).

> @@ -111,7 +115,8 @@ __libdwfl_debuginfod_end (debuginfod_client *c)
>  }
>  
>  /* Try to get the libdebuginfod library functions.
> -   Only needs to be called once from dwfl_get_debuginfod_client.  */
> +   Only needs to be called once from dwfl_get_debuginfod_client.
> +   A per-Dwfl lock must be held when calling this function.  */
>  static void
>  __libdwfl_debuginfod_init (void)
>  {

Why does it need a per-Dwfl lock? It is already guarded by a
pthread_once. And it doesn't seem to manipulate any Dwfl struct field
structures (and cannot because it isn't passed a Dwfl object).


Cheers,

Mark
  
Aaron Merey Dec. 4, 2025, 11:52 p.m. UTC | #3
Hi Frank,

On Mon, Dec 1, 2025 at 4:44 PM Frank Ch. Eigler <fche@redhat.com> wrote:
>
> > Individual debuginfod_client objects and their underlying CURLM handles
> > should not be used concurrently [...]
>
> Yeah, pity eh!  libdebuginfod is perfectly happy with concurrent
> calls, but only on different debuginfod_client handles, rather than
> the single shared one tracked by libdwfl.  It's unlikely to be worth
> the effort to keep a pool of such handles to be shared among threads
> ... but maybe data could answer that question.

Interesting idea, the upcoming systemtap parallelization work will
provide a nice way to test this.

Aaron
  
Aaron Merey Dec. 5, 2025, 12:03 a.m. UTC | #4
Hi Mark,

On Wed, Dec 3, 2025 at 11:19 AM Mark Wielaard <mark@klomp.org> wrote:
>
> On Mon, 2025-12-01 at 12:17 -0500, Aaron Merey wrote:
> > Individual debuginfod_client objects and their underlying CURLM handles
> > should not be used concurrently during calls to
> > __libdwfl_debuginfod_find_debuginfo and
> > __libdwfl_debuginfod_find_executable.
> >
> > Add a mutex field to struct Dwfl and synchronize calls to
> > __libdwfl_debuginfod_find_*.
>
> So if I understand correctly there are two issues here.
>
> The debuginfo_client is created and assigned to a Dwfl field lazily. So
> you want to prevent concurrent calls to dwfl_get_debuginfod_client.
>
> A debuginfo_client has CURLM handle which cannot be used by different
> threads concurrently.
>
> Since we have one unique debuginfo_client per dwfl you use one and the
> same lock?
>
> I believe this patch should work. But have you considered having a lock
> inside debuginfod-client itself to protect access to the CURLM handles?
> That would (maybe) result is slightly simpler code. And would help
> other users of libdebuginfod that might use debuginfod_client handles
> from multiple threads (gdb?).

I agree that it is better to have dedicated per-client locks instead.
Theres no need for libdwfl functions to wait on locks held for
independent debuginfod purposes.

>
> > @@ -111,7 +115,8 @@ __libdwfl_debuginfod_end (debuginfod_client *c)
> >  }
> >
> >  /* Try to get the libdebuginfod library functions.
> > -   Only needs to be called once from dwfl_get_debuginfod_client.  */
> > +   Only needs to be called once from dwfl_get_debuginfod_client.
> > +   A per-Dwfl lock must be held when calling this function.  */
> >  static void
> >  __libdwfl_debuginfod_init (void)
> >  {
>
> Why does it need a per-Dwfl lock? It is already guarded by a
> pthread_once. And it doesn't seem to manipulate any Dwfl struct field
> structures (and cannot because it isn't passed a Dwfl object).

You're right since this function is only ever used by pthread_once. I
had general usage of __libdwfl_debuginfod_init in mind when I wrote
that but it's not relevant in this case.

Aaron
  

Patch

diff --git a/libdwfl/debuginfod-client.c b/libdwfl/debuginfod-client.c
index 882a5eff..4b457da4 100644
--- a/libdwfl/debuginfod-client.c
+++ b/libdwfl/debuginfod-client.c
@@ -49,7 +49,7 @@  static void __libdwfl_debuginfod_init (void);
 
 static pthread_once_t init_control = PTHREAD_ONCE_INIT;
 
-/* NB: this is slightly thread-unsafe */
+/* A per-Dwfl lock must be held when calling this function.  */
 
 debuginfod_client *
 dwfl_get_debuginfod_client (Dwfl *dwfl)
@@ -77,10 +77,12 @@  __libdwfl_debuginfod_find_executable (Dwfl *dwfl,
   int fd = -1;
   if (build_id_len > 0)
     {
+      mutex_lock (dwfl->lock);
       debuginfod_client *c = INTUSE (dwfl_get_debuginfod_client) (dwfl);
       if (c != NULL)
 	fd = (*fp_debuginfod_find_executable) (c, build_id_bits,
 					       build_id_len, NULL);
+      mutex_unlock (dwfl->lock);
     }
 
   return fd;
@@ -94,10 +96,12 @@  __libdwfl_debuginfod_find_debuginfo (Dwfl *dwfl,
   int fd = -1;
   if (build_id_len > 0)
     {
+      mutex_lock (dwfl->lock);
       debuginfod_client *c = INTUSE (dwfl_get_debuginfod_client) (dwfl);
       if (c != NULL)
 	fd = (*fp_debuginfod_find_debuginfo) (c, build_id_bits,
 					      build_id_len, NULL);
+      mutex_unlock (dwfl->lock);
     }
 
   return fd;
@@ -111,7 +115,8 @@  __libdwfl_debuginfod_end (debuginfod_client *c)
 }
 
 /* Try to get the libdebuginfod library functions.
-   Only needs to be called once from dwfl_get_debuginfod_client.  */
+   Only needs to be called once from dwfl_get_debuginfod_client.
+   A per-Dwfl lock must be held when calling this function.  */
 static void
 __libdwfl_debuginfod_init (void)
 {
diff --git a/libdwfl/dwfl_begin.c b/libdwfl/dwfl_begin.c
index b03f5cf4..71947851 100644
--- a/libdwfl/dwfl_begin.c
+++ b/libdwfl/dwfl_begin.c
@@ -50,6 +50,8 @@  dwfl_begin (const Dwfl_Callbacks *callbacks)
       dwfl->offline_next_address = OFFLINE_REDZONE;
     }
 
+  mutex_init (dwfl->lock);
+
   return dwfl;
 }
 INTDEF (dwfl_begin)
diff --git a/libdwfl/dwfl_end.c b/libdwfl/dwfl_end.c
index d9cf569b..594051f0 100644
--- a/libdwfl/dwfl_end.c
+++ b/libdwfl/dwfl_end.c
@@ -53,6 +53,7 @@  dwfl_end (Dwfl *dwfl)
   free (dwfl->lookup_module);
   free (dwfl->lookup_segndx);
   free (dwfl->sysroot);
+  mutex_fini (dwfl->lock);
 
   Dwfl_Module *next = dwfl->modulelist;
   while (next != NULL)
diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h
index a5d88d60..341c0527 100644
--- a/libdwfl/libdwflP.h
+++ b/libdwfl/libdwflP.h
@@ -141,6 +141,7 @@  struct Dwfl
   struct Dwfl_User_Core *user_core;
   char *sysroot;		/* sysroot, or NULL to search standard system
 				   paths */
+  mutex_define (, lock);	/* Synchronize concurrent access.  */
 };
 
 #define OFFLINE_REDZONE		0x10000