On Thu, 24 Jul 2025 at 10:13:08 +0100, Simon McVittie wrote:
On Tue, 22 Jul 2025 at 13:32:34 +0100, Nick Steel wrote:
most of Mopidy's value comes from extensions (mostly distributed via PyPI
etc) and it's these that are exposed to the bug, some are consequently
rendered unusable

How can this deadlock be reproduced on a Debian system, preferably with software from Debian only, or with third-party code if necessary?

GModule uses a recursive lock, so the most obvious reproducers like

   g_module_open_full ("/usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstsoup.so", 
0, error);

do not deadlock: any reproducer would need to be multithreaded.

I was able to reproduce this with the attached artificial test-case. (Good result: it runs for 10 seconds of high CPU load and then exits gracefully. Bad result: it continues to run indefinitely, with CPU use dropping to zero when it becomes deadlocked.)

I confirm that the upstream change makes the test-case work as it should.

    smcv
/*
gcc -o1109685 1109685.c $(pkgconf --cflags --libs gmodule-2.0)
gdb ./1109685
*/

#include <dlfcn.h>

#include <gmodule.h>

#define RUN_SECONDS 10

static void *
gmodule_thread_cb (void *nil)
{
  g_autoptr(GError) error = NULL;
  /* Spam GModule calls to try to reproduce #1109685 */
  GModule *m;
  gint64 start, now, end;
  void *free_fn;

  g_printerr ("Starting GModule thread\n");

  m = g_module_open_full ("libc.so.6", 0, &error);

  if (m == NULL)
    g_error ("%s", error->message);

  start = g_get_monotonic_time ();
  end = start + RUN_SECONDS * G_USEC_PER_SEC;
  g_printerr ("Starting GModule load generation\n");

  for (now = g_get_monotonic_time ();
       now < end;
       now = g_get_monotonic_time ())
    g_module_symbol (m, "free", &free_fn);

  g_printerr ("Finished GModule load generation\n");
  g_module_close (m);
  return NULL;
}

static void *
dlopen_thread_cb (void *nil)
{
  /* Spam dlopen calls to try to reproduce #1109685 */
  gint64 start, now, end;
  void *handle;

  start = g_get_monotonic_time ();
  end = start + RUN_SECONDS * G_USEC_PER_SEC;
  g_printerr ("Starting dlopen load generation\n");

  for (now = g_get_monotonic_time ();
       now < end;
       now = g_get_monotonic_time ())
    {
      handle = dlopen ("libsoup-3.0.so.0", RTLD_LAZY | RTLD_LOCAL);
      dlclose (handle);
    }

  g_printerr ("Finished dlopen load generation\n");
  return NULL;
}

int
main(void)
{
  GThread *gmodule_thread;
  GThread *dlopen_thread;

  g_printerr ("Initializing\n");

  gmodule_thread = g_thread_new ("gmodule_thread", gmodule_thread_cb, NULL);
  dlopen_thread = g_thread_new ("dlopen_thread", dlopen_thread_cb, NULL);

  g_printerr ("Waiting for threads\n");
  g_thread_join (gmodule_thread);
  g_thread_join (dlopen_thread);
  g_printerr ("Finished\n");
  return 0;
}

Reply via email to