From fb45e0aca9d0bf43d3086fc28e9e5bc66fe6500b Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Thu, 6 Jul 2023 19:24:33 +0200 Subject: Test case for bug 30619 --- sysdeps/pthread/Makefile | 9 ++ sysdeps/pthread/tst-thread-dlopen-fork-mod.c | 1 + sysdeps/pthread/tst-thread-dlopen-fork.c | 133 +++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 sysdeps/pthread/tst-thread-dlopen-fork-mod.c create mode 100644 sysdeps/pthread/tst-thread-dlopen-fork.c diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile index 32cf4eb119..38b0a66d3c 100644 --- a/sysdeps/pthread/Makefile +++ b/sysdeps/pthread/Makefile @@ -335,6 +335,7 @@ tests += \ tst-atfork3 \ tst-atfork4 \ tst-create1 \ + tst-thread-dlopen-fork \ tst-fini1 \ tst-pt-tls4 \ # tests @@ -351,6 +352,7 @@ modules-names += \ tst-atfork3mod \ tst-atfork4mod \ tst-create1mod \ + tst-thread-dlopen-fork-mod \ tst-fini1mod \ tst-tls4moda \ tst-tls4modb \ @@ -525,4 +527,11 @@ LDFLAGS-tst-create1 = -Wl,-export-dynamic $(objpfx)tst-create1: $(shared-thread-library) $(objpfx)tst-create1.out: $(objpfx)tst-create1mod.so +# Create multiple shared objects to be used by the test. +tst-thread-dlopen-fork.sos := \ + $(patsubst %,$(objpfx)tst-thread-dlopen-fork-mod-%.so,$(shell seq 0 16)) +$(tst-thread-dlopen-fork.sos): $(objpfx)tst-thread-dlopen-fork-mod.so + cp -f $< $@ +$(objpfx)tst-thread-dlopen-fork.out: $(tst-thread-dlopen-fork.sos) + endif diff --git a/sysdeps/pthread/tst-thread-dlopen-fork-mod.c b/sysdeps/pthread/tst-thread-dlopen-fork-mod.c new file mode 100644 index 0000000000..c219328239 --- /dev/null +++ b/sysdeps/pthread/tst-thread-dlopen-fork-mod.c @@ -0,0 +1 @@ +/* Empty shared object. */ diff --git a/sysdeps/pthread/tst-thread-dlopen-fork.c b/sysdeps/pthread/tst-thread-dlopen-fork.c new file mode 100644 index 0000000000..53d1279b43 --- /dev/null +++ b/sysdeps/pthread/tst-thread-dlopen-fork.c @@ -0,0 +1,133 @@ +/* Test that dlopen works after concurrent fork. + Copyright (C) 2023 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Use atomics to make sure that issues with dlopen/fork come from + implementation problems, and not from exposing the handle of a + partially initialized link map after dlopen returned. */ +static void *_Atomic handles[17]; + +/* Set to true if the dlopen thread has exited. */ +static _Atomic bool requested_exit; + +/* Used to start the forking and the dlopen thread at the same time. */ +static pthread_barrier_t barrier; + +/* Returns the file to open for IDX. */ +static char * +dso_name (int idx) +{ + return xasprintf ("tst-thread-dlopen-fork-mod-%d.so", idx); +} + +static void * +forking_thread (void *closure) +{ + xpthread_barrier_wait (&barrier); + int mode = 0; + while (!requested_exit) + { + pid_t pid = xfork (); + if (pid == 0) + { + switch (mode) + { + case 0: + /* Try to open all handles. */ + for (int i = 0; i < array_length (handles); ++i) + { + char *name = dso_name (i); + void *handle = xdlopen (name, RTLD_LAZY); + if (handles[i] != NULL) + TEST_VERIFY (handle == handles[i]); + free (name); + } + mode = 1; + break; + case 1: + /* Try to open all unopened handles. */ + for (int i = 0; i < array_length (handles); ++i) + if (handles[i] == NULL) + { + char *name = dso_name (i); + xdlopen (name, RTLD_LAZY); + free (name); + } + mode = 2; + break; + case 2: + /* Try to close all opened handles. */ + for (int i = 0; i < array_length (handles); ++i) + if (handles[i] != NULL) + xdlclose (handles[i]); + break; + } + _exit (0); + } + int status; + xwaitpid (pid, &status, 0); + TEST_COMPARE (status, 0); + } + return NULL; +} + +static void * +dlopen_thread (void *closure) +{ + xpthread_barrier_wait (&barrier); + srand (1); + for (int i = 0; i <= 1000; ++i) + { + int idx = rand () % array_length (handles); + void *handle = handles[idx]; + if (handle == NULL) + { + char *name = dso_name (idx); + handles[idx] = xdlopen (name, RTLD_LAZY); + free (name); + } + else + { + handles[idx] = NULL; + xdlclose (handle); + } + } + return NULL; +} + +static int +do_test (void) +{ + xpthread_barrier_init (&barrier, NULL, 2); + pthread_t thr_fork = xpthread_create (NULL, forking_thread, NULL); + pthread_t thr_dlopen = xpthread_create (NULL, dlopen_thread, NULL); + xpthread_join (thr_dlopen); + requested_exit = true; + xpthread_join (thr_fork); + return 0; +} + +#include -- cgit 1.4.1