|
|
548bcb |
commit 83b5323261bb72313bffcf37476c1b8f0847c736
|
|
|
548bcb |
Author: Szabolcs Nagy <szabolcs.nagy@arm.com>
|
|
|
548bcb |
Date: Wed Sep 15 15:16:19 2021 +0100
|
|
|
548bcb |
|
|
|
548bcb |
elf: Avoid deadlock between pthread_create and ctors [BZ #28357]
|
|
|
548bcb |
|
|
|
548bcb |
The fix for bug 19329 caused a regression such that pthread_create can
|
|
|
548bcb |
deadlock when concurrent ctors from dlopen are waiting for it to finish.
|
|
|
548bcb |
Use a new GL(dl_load_tls_lock) in pthread_create that is not taken
|
|
|
548bcb |
around ctors in dlopen.
|
|
|
548bcb |
|
|
|
548bcb |
The new lock is also used in __tls_get_addr instead of GL(dl_load_lock).
|
|
|
548bcb |
|
|
|
548bcb |
The new lock is held in _dl_open_worker and _dl_close_worker around
|
|
|
548bcb |
most of the logic before/after the init/fini routines. When init/fini
|
|
|
548bcb |
routines are running then TLS is in a consistent, usable state.
|
|
|
548bcb |
In _dl_open_worker the new lock requires catching and reraising dlopen
|
|
|
548bcb |
failures that happen in the critical section.
|
|
|
548bcb |
|
|
|
548bcb |
The new lock is reinitialized in a fork child, to keep the existing
|
|
|
548bcb |
behaviour and it is kept recursive in case malloc interposition or TLS
|
|
|
548bcb |
access from signal handlers can retake it. It is not obvious if this
|
|
|
548bcb |
is necessary or helps, but avoids changing the preexisting behaviour.
|
|
|
548bcb |
|
|
|
548bcb |
The new lock may be more appropriate for dl_iterate_phdr too than
|
|
|
548bcb |
GL(dl_load_write_lock), since TLS state of an incompletely loaded
|
|
|
548bcb |
module may be accessed. If the new lock can replace the old one,
|
|
|
548bcb |
that can be a separate change.
|
|
|
548bcb |
|
|
|
548bcb |
Fixes bug 28357.
|
|
|
548bcb |
|
|
|
548bcb |
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
|
|
548bcb |
|
|
|
548bcb |
Conflicts:
|
|
|
548bcb |
posix/fork.c
|
|
|
548bcb |
(reworked due to file rename upstream and libpthread integration)
|
|
|
548bcb |
sysdeps/pthread/Makefile
|
|
|
548bcb |
(htl testing support was missing downstream, reconstituted here;
|
|
|
548bcb |
added $(libdl) required downstream)
|
|
|
548bcb |
|
|
|
548bcb |
diff --git a/elf/dl-close.c b/elf/dl-close.c
|
|
|
548bcb |
index 18227fe992029364..7fe91bdd9aaf694e 100644
|
|
|
548bcb |
--- a/elf/dl-close.c
|
|
|
548bcb |
+++ b/elf/dl-close.c
|
|
|
548bcb |
@@ -549,6 +549,9 @@ _dl_close_worker (struct link_map *map, bool force)
|
|
|
548bcb |
size_t tls_free_end;
|
|
|
548bcb |
tls_free_start = tls_free_end = NO_TLS_OFFSET;
|
|
|
548bcb |
|
|
|
548bcb |
+ /* Protects global and module specitic TLS state. */
|
|
|
548bcb |
+ __rtld_lock_lock_recursive (GL(dl_load_tls_lock));
|
|
|
548bcb |
+
|
|
|
548bcb |
/* We modify the list of loaded objects. */
|
|
|
548bcb |
__rtld_lock_lock_recursive (GL(dl_load_write_lock));
|
|
|
548bcb |
|
|
|
548bcb |
@@ -784,6 +787,9 @@ _dl_close_worker (struct link_map *map, bool force)
|
|
|
548bcb |
GL(dl_tls_static_used) = tls_free_start;
|
|
|
548bcb |
}
|
|
|
548bcb |
|
|
|
548bcb |
+ /* TLS is cleaned up for the unloaded modules. */
|
|
|
548bcb |
+ __rtld_lock_unlock_recursive (GL(dl_load_tls_lock));
|
|
|
548bcb |
+
|
|
|
548bcb |
#ifdef SHARED
|
|
|
548bcb |
/* Auditing checkpoint: we have deleted all objects. */
|
|
|
548bcb |
if (__glibc_unlikely (do_audit))
|
|
|
548bcb |
diff --git a/elf/dl-open.c b/elf/dl-open.c
|
|
|
548bcb |
index 54727402750f4c0c..736df62ce6e46d34 100644
|
|
|
548bcb |
--- a/elf/dl-open.c
|
|
|
548bcb |
+++ b/elf/dl-open.c
|
|
|
548bcb |
@@ -65,6 +65,9 @@ struct dl_open_args
|
|
|
548bcb |
libc_map value in the namespace in case of a dlopen failure. */
|
|
|
548bcb |
bool libc_already_loaded;
|
|
|
548bcb |
|
|
|
548bcb |
+ /* Set to true if the end of dl_open_worker_begin was reached. */
|
|
|
548bcb |
+ bool worker_continue;
|
|
|
548bcb |
+
|
|
|
548bcb |
/* Original parameters to the program and the current environment. */
|
|
|
548bcb |
int argc;
|
|
|
548bcb |
char **argv;
|
|
|
548bcb |
@@ -481,7 +484,7 @@ call_dl_init (void *closure)
|
|
|
548bcb |
}
|
|
|
548bcb |
|
|
|
548bcb |
static void
|
|
|
548bcb |
-dl_open_worker (void *a)
|
|
|
548bcb |
+dl_open_worker_begin (void *a)
|
|
|
548bcb |
{
|
|
|
548bcb |
struct dl_open_args *args = a;
|
|
|
548bcb |
const char *file = args->file;
|
|
|
548bcb |
@@ -772,6 +775,36 @@ dl_open_worker (void *a)
|
|
|
548bcb |
DL_STATIC_INIT (new);
|
|
|
548bcb |
#endif
|
|
|
548bcb |
|
|
|
548bcb |
+ args->worker_continue = true;
|
|
|
548bcb |
+}
|
|
|
548bcb |
+
|
|
|
548bcb |
+static void
|
|
|
548bcb |
+dl_open_worker (void *a)
|
|
|
548bcb |
+{
|
|
|
548bcb |
+ struct dl_open_args *args = a;
|
|
|
548bcb |
+
|
|
|
548bcb |
+ args->worker_continue = false;
|
|
|
548bcb |
+
|
|
|
548bcb |
+ {
|
|
|
548bcb |
+ /* Protects global and module specific TLS state. */
|
|
|
548bcb |
+ __rtld_lock_lock_recursive (GL(dl_load_tls_lock));
|
|
|
548bcb |
+
|
|
|
548bcb |
+ struct dl_exception ex;
|
|
|
548bcb |
+ int err = _dl_catch_exception (&ex, dl_open_worker_begin, args);
|
|
|
548bcb |
+
|
|
|
548bcb |
+ __rtld_lock_unlock_recursive (GL(dl_load_tls_lock));
|
|
|
548bcb |
+
|
|
|
548bcb |
+ if (__glibc_unlikely (ex.errstring != NULL))
|
|
|
548bcb |
+ /* Reraise the error. */
|
|
|
548bcb |
+ _dl_signal_exception (err, &ex, NULL);
|
|
|
548bcb |
+ }
|
|
|
548bcb |
+
|
|
|
548bcb |
+ if (!args->worker_continue)
|
|
|
548bcb |
+ return;
|
|
|
548bcb |
+
|
|
|
548bcb |
+ int mode = args->mode;
|
|
|
548bcb |
+ struct link_map *new = args->map;
|
|
|
548bcb |
+
|
|
|
548bcb |
/* Run the initializer functions of new objects. Temporarily
|
|
|
548bcb |
disable the exception handler, so that lazy binding failures are
|
|
|
548bcb |
fatal. */
|
|
|
548bcb |
diff --git a/elf/dl-support.c b/elf/dl-support.c
|
|
|
548bcb |
index 34be8e5babfb6af3..3e5531138eaa18f8 100644
|
|
|
548bcb |
--- a/elf/dl-support.c
|
|
|
548bcb |
+++ b/elf/dl-support.c
|
|
|
548bcb |
@@ -212,6 +212,13 @@ __rtld_lock_define_initialized_recursive (, _dl_load_lock)
|
|
|
548bcb |
list of loaded objects while an object is added to or removed from
|
|
|
548bcb |
that list. */
|
|
|
548bcb |
__rtld_lock_define_initialized_recursive (, _dl_load_write_lock)
|
|
|
548bcb |
+ /* This lock protects global and module specific TLS related data.
|
|
|
548bcb |
+ E.g. it is held in dlopen and dlclose when GL(dl_tls_generation),
|
|
|
548bcb |
+ GL(dl_tls_max_dtv_idx) or GL(dl_tls_dtv_slotinfo_list) are
|
|
|
548bcb |
+ accessed and when TLS related relocations are processed for a
|
|
|
548bcb |
+ module. It was introduced to keep pthread_create accessing TLS
|
|
|
548bcb |
+ state that is being set up. */
|
|
|
548bcb |
+__rtld_lock_define_initialized_recursive (, _dl_load_tls_lock)
|
|
|
548bcb |
|
|
|
548bcb |
|
|
|
548bcb |
#ifdef HAVE_AUX_VECTOR
|
|
|
548bcb |
diff --git a/elf/dl-tls.c b/elf/dl-tls.c
|
|
|
548bcb |
index 8c0f9e972d7a0eac..7865fc390c3f3f0a 100644
|
|
|
548bcb |
--- a/elf/dl-tls.c
|
|
|
548bcb |
+++ b/elf/dl-tls.c
|
|
|
548bcb |
@@ -527,7 +527,7 @@ _dl_allocate_tls_init (void *result)
|
|
|
548bcb |
size_t maxgen = 0;
|
|
|
548bcb |
|
|
|
548bcb |
/* Protects global dynamic TLS related state. */
|
|
|
548bcb |
- __rtld_lock_lock_recursive (GL(dl_load_lock));
|
|
|
548bcb |
+ __rtld_lock_lock_recursive (GL(dl_load_tls_lock));
|
|
|
548bcb |
|
|
|
548bcb |
/* Check if the current dtv is big enough. */
|
|
|
548bcb |
if (dtv[-1].counter < GL(dl_tls_max_dtv_idx))
|
|
|
548bcb |
@@ -601,7 +601,7 @@ _dl_allocate_tls_init (void *result)
|
|
|
548bcb |
listp = listp->next;
|
|
|
548bcb |
assert (listp != NULL);
|
|
|
548bcb |
}
|
|
|
548bcb |
- __rtld_lock_unlock_recursive (GL(dl_load_lock));
|
|
|
548bcb |
+ __rtld_lock_unlock_recursive (GL(dl_load_tls_lock));
|
|
|
548bcb |
|
|
|
548bcb |
/* The DTV version is up-to-date now. */
|
|
|
548bcb |
dtv[0].counter = maxgen;
|
|
|
548bcb |
@@ -740,7 +740,7 @@ _dl_update_slotinfo (unsigned long int req_modid)
|
|
|
548bcb |
|
|
|
548bcb |
Here the dtv needs to be updated to new_gen generation count.
|
|
|
548bcb |
|
|
|
548bcb |
- This code may be called during TLS access when GL(dl_load_lock)
|
|
|
548bcb |
+ This code may be called during TLS access when GL(dl_load_tls_lock)
|
|
|
548bcb |
is not held. In that case the user code has to synchronize with
|
|
|
548bcb |
dlopen and dlclose calls of relevant modules. A module m is
|
|
|
548bcb |
relevant if the generation of m <= new_gen and dlclose of m is
|
|
|
548bcb |
@@ -862,11 +862,11 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map)
|
|
|
548bcb |
if (__glibc_unlikely (the_map->l_tls_offset
|
|
|
548bcb |
!= FORCED_DYNAMIC_TLS_OFFSET))
|
|
|
548bcb |
{
|
|
|
548bcb |
- __rtld_lock_lock_recursive (GL(dl_load_lock));
|
|
|
548bcb |
+ __rtld_lock_lock_recursive (GL(dl_load_tls_lock));
|
|
|
548bcb |
if (__glibc_likely (the_map->l_tls_offset == NO_TLS_OFFSET))
|
|
|
548bcb |
{
|
|
|
548bcb |
the_map->l_tls_offset = FORCED_DYNAMIC_TLS_OFFSET;
|
|
|
548bcb |
- __rtld_lock_unlock_recursive (GL(dl_load_lock));
|
|
|
548bcb |
+ __rtld_lock_unlock_recursive (GL(dl_load_tls_lock));
|
|
|
548bcb |
}
|
|
|
548bcb |
else if (__glibc_likely (the_map->l_tls_offset
|
|
|
548bcb |
!= FORCED_DYNAMIC_TLS_OFFSET))
|
|
|
548bcb |
@@ -878,7 +878,7 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map)
|
|
|
548bcb |
#else
|
|
|
548bcb |
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
|
|
|
548bcb |
#endif
|
|
|
548bcb |
- __rtld_lock_unlock_recursive (GL(dl_load_lock));
|
|
|
548bcb |
+ __rtld_lock_unlock_recursive (GL(dl_load_tls_lock));
|
|
|
548bcb |
|
|
|
548bcb |
dtv[GET_ADDR_MODULE].pointer.to_free = NULL;
|
|
|
548bcb |
dtv[GET_ADDR_MODULE].pointer.val = p;
|
|
|
548bcb |
@@ -886,7 +886,7 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map)
|
|
|
548bcb |
return (char *) p + GET_ADDR_OFFSET;
|
|
|
548bcb |
}
|
|
|
548bcb |
else
|
|
|
548bcb |
- __rtld_lock_unlock_recursive (GL(dl_load_lock));
|
|
|
548bcb |
+ __rtld_lock_unlock_recursive (GL(dl_load_tls_lock));
|
|
|
548bcb |
}
|
|
|
548bcb |
struct dtv_pointer result = allocate_and_init (the_map);
|
|
|
548bcb |
dtv[GET_ADDR_MODULE].pointer = result;
|
|
|
548bcb |
@@ -957,7 +957,7 @@ _dl_tls_get_addr_soft (struct link_map *l)
|
|
|
548bcb |
return NULL;
|
|
|
548bcb |
|
|
|
548bcb |
dtv_t *dtv = THREAD_DTV ();
|
|
|
548bcb |
- /* This may be called without holding the GL(dl_load_lock). Reading
|
|
|
548bcb |
+ /* This may be called without holding the GL(dl_load_tls_lock). Reading
|
|
|
548bcb |
arbitrary gen value is fine since this is best effort code. */
|
|
|
548bcb |
size_t gen = atomic_load_relaxed (&GL(dl_tls_generation));
|
|
|
548bcb |
if (__glibc_unlikely (dtv[0].counter != gen))
|
|
|
548bcb |
diff --git a/elf/rtld.c b/elf/rtld.c
|
|
|
548bcb |
index 118c454a2329573f..9e09896da078274d 100644
|
|
|
548bcb |
--- a/elf/rtld.c
|
|
|
548bcb |
+++ b/elf/rtld.c
|
|
|
548bcb |
@@ -317,6 +317,7 @@ struct rtld_global _rtld_global =
|
|
|
548bcb |
#ifdef _LIBC_REENTRANT
|
|
|
548bcb |
._dl_load_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER,
|
|
|
548bcb |
._dl_load_write_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER,
|
|
|
548bcb |
+ ._dl_load_tls_lock = _RTLD_LOCK_RECURSIVE_INITIALIZER,
|
|
|
548bcb |
#endif
|
|
|
548bcb |
._dl_nns = 1,
|
|
|
548bcb |
._dl_ns =
|
|
|
548bcb |
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
|
|
|
548bcb |
index 0138353ccb41c5f1..7b0a667629ddc06a 100644
|
|
|
548bcb |
--- a/sysdeps/generic/ldsodefs.h
|
|
|
548bcb |
+++ b/sysdeps/generic/ldsodefs.h
|
|
|
548bcb |
@@ -373,6 +373,13 @@ struct rtld_global
|
|
|
548bcb |
list of loaded objects while an object is added to or removed
|
|
|
548bcb |
from that list. */
|
|
|
548bcb |
__rtld_lock_define_recursive (EXTERN, _dl_load_write_lock)
|
|
|
548bcb |
+ /* This lock protects global and module specific TLS related data.
|
|
|
548bcb |
+ E.g. it is held in dlopen and dlclose when GL(dl_tls_generation),
|
|
|
548bcb |
+ GL(dl_tls_max_dtv_idx) or GL(dl_tls_dtv_slotinfo_list) are
|
|
|
548bcb |
+ accessed and when TLS related relocations are processed for a
|
|
|
548bcb |
+ module. It was introduced to keep pthread_create accessing TLS
|
|
|
548bcb |
+ state that is being set up. */
|
|
|
548bcb |
+ __rtld_lock_define_recursive (EXTERN, _dl_load_tls_lock)
|
|
|
548bcb |
|
|
|
548bcb |
/* Incremented whenever something may have been added to dl_loaded. */
|
|
|
548bcb |
EXTERN unsigned long long _dl_load_adds;
|
|
|
548bcb |
@@ -1192,7 +1199,7 @@ extern int _dl_scope_free (void *) attribute_hidden;
|
|
|
548bcb |
|
|
|
548bcb |
/* Add module to slot information data. If DO_ADD is false, only the
|
|
|
548bcb |
required memory is allocated. Must be called with GL
|
|
|
548bcb |
- (dl_load_lock) acquired. If the function has already been called
|
|
|
548bcb |
+ (dl_load_tls_lock) acquired. If the function has already been called
|
|
|
548bcb |
for the link map L with !do_add, then this function will not raise
|
|
|
548bcb |
an exception, otherwise it is possible that it encounters a memory
|
|
|
548bcb |
allocation failure. */
|
|
|
548bcb |
diff --git a/sysdeps/nptl/fork.c b/sysdeps/nptl/fork.c
|
|
|
548bcb |
index 37db30f3d1e846b6..b4d20fa652f4ba3b 100644
|
|
|
548bcb |
--- a/sysdeps/nptl/fork.c
|
|
|
548bcb |
+++ b/sysdeps/nptl/fork.c
|
|
|
548bcb |
@@ -125,6 +125,9 @@ __libc_fork (void)
|
|
|
548bcb |
/* Reset the lock the dynamic loader uses to protect its data. */
|
|
|
548bcb |
__rtld_lock_initialize (GL(dl_load_lock));
|
|
|
548bcb |
|
|
|
548bcb |
+ /* Reset the lock protecting dynamic TLS related data. */
|
|
|
548bcb |
+ __rtld_lock_initialize (GL(dl_load_tls_lock));
|
|
|
548bcb |
+
|
|
|
548bcb |
/* Run the handlers registered for the child. */
|
|
|
548bcb |
__run_fork_handlers (atfork_run_child, multiple_threads);
|
|
|
548bcb |
}
|
|
|
548bcb |
diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile
|
|
|
548bcb |
index ea4f8894891b2636..98a92f8d6bb119ba 100644
|
|
|
548bcb |
--- a/sysdeps/pthread/Makefile
|
|
|
548bcb |
+++ b/sysdeps/pthread/Makefile
|
|
|
548bcb |
@@ -25,3 +25,24 @@ $(objpfx)tst-timer: $(objpfx)librt.a $(static-thread-library)
|
|
|
548bcb |
endif
|
|
|
548bcb |
|
|
|
548bcb |
endif
|
|
|
548bcb |
+
|
|
|
548bcb |
+ifneq (,$(filter $(subdir),htl nptl))
|
|
|
548bcb |
+ifeq ($(build-shared),yes)
|
|
|
548bcb |
+tests += tst-create1
|
|
|
548bcb |
+endif
|
|
|
548bcb |
+
|
|
|
548bcb |
+tst-create1mod.so-no-z-defs = yes
|
|
|
548bcb |
+
|
|
|
548bcb |
+ifeq ($(build-shared),yes)
|
|
|
548bcb |
+# Build all the modules even when not actually running test programs.
|
|
|
548bcb |
+tests: $(test-modules)
|
|
|
548bcb |
+endif
|
|
|
548bcb |
+
|
|
|
548bcb |
+modules-names += tst-create1mod
|
|
|
548bcb |
+test-modules = $(addprefix $(objpfx),$(addsuffix .so,$(modules-names)))
|
|
|
548bcb |
+
|
|
|
548bcb |
+LDFLAGS-tst-create1 = -Wl,-export-dynamic
|
|
|
548bcb |
+$(objpfx)tst-create1: $(libdl) $(shared-thread-library)
|
|
|
548bcb |
+$(objpfx)tst-create1.out: $(objpfx)tst-create1mod.so
|
|
|
548bcb |
+
|
|
|
548bcb |
+endif
|
|
|
548bcb |
diff --git a/sysdeps/pthread/tst-create1.c b/sysdeps/pthread/tst-create1.c
|
|
|
548bcb |
new file mode 100644
|
|
|
548bcb |
index 0000000000000000..932586c30990d1d4
|
|
|
548bcb |
--- /dev/null
|
|
|
548bcb |
+++ b/sysdeps/pthread/tst-create1.c
|
|
|
548bcb |
@@ -0,0 +1,119 @@
|
|
|
548bcb |
+/* Verify that pthread_create does not deadlock when ctors take locks.
|
|
|
548bcb |
+ Copyright (C) 2021 Free Software Foundation, Inc.
|
|
|
548bcb |
+ This file is part of the GNU C Library.
|
|
|
548bcb |
+
|
|
|
548bcb |
+ The GNU C Library is free software; you can redistribute it and/or
|
|
|
548bcb |
+ modify it under the terms of the GNU Lesser General Public
|
|
|
548bcb |
+ License as published by the Free Software Foundation; either
|
|
|
548bcb |
+ version 2.1 of the License, or (at your option) any later version.
|
|
|
548bcb |
+
|
|
|
548bcb |
+ The GNU C Library is distributed in the hope that it will be useful,
|
|
|
548bcb |
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
548bcb |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
548bcb |
+ Lesser General Public License for more details.
|
|
|
548bcb |
+
|
|
|
548bcb |
+ You should have received a copy of the GNU Lesser General Public
|
|
|
548bcb |
+ License along with the GNU C Library; if not, see
|
|
|
548bcb |
+ <https://www.gnu.org/licenses/>. */
|
|
|
548bcb |
+
|
|
|
548bcb |
+#include <stdio.h>
|
|
|
548bcb |
+#include <support/xdlfcn.h>
|
|
|
548bcb |
+#include <support/xthread.h>
|
|
|
548bcb |
+
|
|
|
548bcb |
+/*
|
|
|
548bcb |
+Check if ctor and pthread_create deadlocks in
|
|
|
548bcb |
+
|
|
|
548bcb |
+thread 1: dlopen -> ctor -> lock(user_lock)
|
|
|
548bcb |
+thread 2: lock(user_lock) -> pthread_create
|
|
|
548bcb |
+
|
|
|
548bcb |
+or in
|
|
|
548bcb |
+
|
|
|
548bcb |
+thread 1: dlclose -> dtor -> lock(user_lock)
|
|
|
548bcb |
+thread 2: lock(user_lock) -> pthread_create
|
|
|
548bcb |
+*/
|
|
|
548bcb |
+
|
|
|
548bcb |
+static pthread_barrier_t bar_ctor;
|
|
|
548bcb |
+static pthread_barrier_t bar_dtor;
|
|
|
548bcb |
+static pthread_mutex_t user_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
|
548bcb |
+
|
|
|
548bcb |
+void
|
|
|
548bcb |
+ctor (void)
|
|
|
548bcb |
+{
|
|
|
548bcb |
+ xpthread_barrier_wait (&bar_ctor);
|
|
|
548bcb |
+ dprintf (1, "thread 1: in ctor: started.\n");
|
|
|
548bcb |
+ xpthread_mutex_lock (&user_lock);
|
|
|
548bcb |
+ dprintf (1, "thread 1: in ctor: locked user_lock.\n");
|
|
|
548bcb |
+ xpthread_mutex_unlock (&user_lock);
|
|
|
548bcb |
+ dprintf (1, "thread 1: in ctor: unlocked user_lock.\n");
|
|
|
548bcb |
+ dprintf (1, "thread 1: in ctor: done.\n");
|
|
|
548bcb |
+}
|
|
|
548bcb |
+
|
|
|
548bcb |
+void
|
|
|
548bcb |
+dtor (void)
|
|
|
548bcb |
+{
|
|
|
548bcb |
+ xpthread_barrier_wait (&bar_dtor);
|
|
|
548bcb |
+ dprintf (1, "thread 1: in dtor: started.\n");
|
|
|
548bcb |
+ xpthread_mutex_lock (&user_lock);
|
|
|
548bcb |
+ dprintf (1, "thread 1: in dtor: locked user_lock.\n");
|
|
|
548bcb |
+ xpthread_mutex_unlock (&user_lock);
|
|
|
548bcb |
+ dprintf (1, "thread 1: in dtor: unlocked user_lock.\n");
|
|
|
548bcb |
+ dprintf (1, "thread 1: in dtor: done.\n");
|
|
|
548bcb |
+}
|
|
|
548bcb |
+
|
|
|
548bcb |
+static void *
|
|
|
548bcb |
+thread3 (void *a)
|
|
|
548bcb |
+{
|
|
|
548bcb |
+ dprintf (1, "thread 3: started.\n");
|
|
|
548bcb |
+ dprintf (1, "thread 3: done.\n");
|
|
|
548bcb |
+ return 0;
|
|
|
548bcb |
+}
|
|
|
548bcb |
+
|
|
|
548bcb |
+static void *
|
|
|
548bcb |
+thread2 (void *a)
|
|
|
548bcb |
+{
|
|
|
548bcb |
+ pthread_t t3;
|
|
|
548bcb |
+ dprintf (1, "thread 2: started.\n");
|
|
|
548bcb |
+
|
|
|
548bcb |
+ xpthread_mutex_lock (&user_lock);
|
|
|
548bcb |
+ dprintf (1, "thread 2: locked user_lock.\n");
|
|
|
548bcb |
+ xpthread_barrier_wait (&bar_ctor);
|
|
|
548bcb |
+ t3 = xpthread_create (0, thread3, 0);
|
|
|
548bcb |
+ xpthread_mutex_unlock (&user_lock);
|
|
|
548bcb |
+ dprintf (1, "thread 2: unlocked user_lock.\n");
|
|
|
548bcb |
+ xpthread_join (t3);
|
|
|
548bcb |
+
|
|
|
548bcb |
+ xpthread_mutex_lock (&user_lock);
|
|
|
548bcb |
+ dprintf (1, "thread 2: locked user_lock.\n");
|
|
|
548bcb |
+ xpthread_barrier_wait (&bar_dtor);
|
|
|
548bcb |
+ t3 = xpthread_create (0, thread3, 0);
|
|
|
548bcb |
+ xpthread_mutex_unlock (&user_lock);
|
|
|
548bcb |
+ dprintf (1, "thread 2: unlocked user_lock.\n");
|
|
|
548bcb |
+ xpthread_join (t3);
|
|
|
548bcb |
+
|
|
|
548bcb |
+ dprintf (1, "thread 2: done.\n");
|
|
|
548bcb |
+ return 0;
|
|
|
548bcb |
+}
|
|
|
548bcb |
+
|
|
|
548bcb |
+static void
|
|
|
548bcb |
+thread1 (void)
|
|
|
548bcb |
+{
|
|
|
548bcb |
+ dprintf (1, "thread 1: started.\n");
|
|
|
548bcb |
+ xpthread_barrier_init (&bar_ctor, NULL, 2);
|
|
|
548bcb |
+ xpthread_barrier_init (&bar_dtor, NULL, 2);
|
|
|
548bcb |
+ pthread_t t2 = xpthread_create (0, thread2, 0);
|
|
|
548bcb |
+ void *p = xdlopen ("tst-create1mod.so", RTLD_NOW | RTLD_GLOBAL);
|
|
|
548bcb |
+ dprintf (1, "thread 1: dlopen done.\n");
|
|
|
548bcb |
+ xdlclose (p);
|
|
|
548bcb |
+ dprintf (1, "thread 1: dlclose done.\n");
|
|
|
548bcb |
+ xpthread_join (t2);
|
|
|
548bcb |
+ dprintf (1, "thread 1: done.\n");
|
|
|
548bcb |
+}
|
|
|
548bcb |
+
|
|
|
548bcb |
+static int
|
|
|
548bcb |
+do_test (void)
|
|
|
548bcb |
+{
|
|
|
548bcb |
+ thread1 ();
|
|
|
548bcb |
+ return 0;
|
|
|
548bcb |
+}
|
|
|
548bcb |
+
|
|
|
548bcb |
+#include <support/test-driver.c>
|
|
|
548bcb |
diff --git a/sysdeps/pthread/tst-create1mod.c b/sysdeps/pthread/tst-create1mod.c
|
|
|
548bcb |
new file mode 100644
|
|
|
548bcb |
index 0000000000000000..62c9006961683177
|
|
|
548bcb |
--- /dev/null
|
|
|
548bcb |
+++ b/sysdeps/pthread/tst-create1mod.c
|
|
|
548bcb |
@@ -0,0 +1,41 @@
|
|
|
548bcb |
+/* Verify that pthread_create does not deadlock when ctors take locks.
|
|
|
548bcb |
+ Copyright (C) 2021 Free Software Foundation, Inc.
|
|
|
548bcb |
+ This file is part of the GNU C Library.
|
|
|
548bcb |
+
|
|
|
548bcb |
+ The GNU C Library is free software; you can redistribute it and/or
|
|
|
548bcb |
+ modify it under the terms of the GNU Lesser General Public
|
|
|
548bcb |
+ License as published by the Free Software Foundation; either
|
|
|
548bcb |
+ version 2.1 of the License, or (at your option) any later version.
|
|
|
548bcb |
+
|
|
|
548bcb |
+ The GNU C Library is distributed in the hope that it will be useful,
|
|
|
548bcb |
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
548bcb |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
548bcb |
+ Lesser General Public License for more details.
|
|
|
548bcb |
+
|
|
|
548bcb |
+ You should have received a copy of the GNU Lesser General Public
|
|
|
548bcb |
+ License along with the GNU C Library; if not, see
|
|
|
548bcb |
+ <https://www.gnu.org/licenses/>. */
|
|
|
548bcb |
+
|
|
|
548bcb |
+#include <stdio.h>
|
|
|
548bcb |
+
|
|
|
548bcb |
+/* Require TLS setup for the module. */
|
|
|
548bcb |
+__thread int tlsvar;
|
|
|
548bcb |
+
|
|
|
548bcb |
+void ctor (void);
|
|
|
548bcb |
+void dtor (void);
|
|
|
548bcb |
+
|
|
|
548bcb |
+static void __attribute__ ((constructor))
|
|
|
548bcb |
+do_init (void)
|
|
|
548bcb |
+{
|
|
|
548bcb |
+ dprintf (1, "constructor started: %d.\n", tlsvar++);
|
|
|
548bcb |
+ ctor ();
|
|
|
548bcb |
+ dprintf (1, "constructor done: %d.\n", tlsvar++);
|
|
|
548bcb |
+}
|
|
|
548bcb |
+
|
|
|
548bcb |
+static void __attribute__ ((destructor))
|
|
|
548bcb |
+do_end (void)
|
|
|
548bcb |
+{
|
|
|
548bcb |
+ dprintf (1, "destructor started: %d.\n", tlsvar++);
|
|
|
548bcb |
+ dtor ();
|
|
|
548bcb |
+ dprintf (1, "destructor done: %d.\n", tlsvar++);
|
|
|
548bcb |
+}
|