Message ID | 20210922075110.5050-1-szabolcs.nagy@arm.com |
---|---|
State | New |
Headers | show |
Series | [1/2] elf: Avoid deadlock between pthread_create and ctors [BZ #28357] | expand |
On 22/09/2021 04:51, Szabolcs Nagy via Libc-alpha wrote: > Check if locks in ctors or dtors can cause pthread_create to deadlock. LGTM with some change below. I think it would be better to merge with the fix itself. Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org> > --- > sysdeps/pthread/Makefile | 10 ++- > sysdeps/pthread/tst-create1.c | 120 +++++++++++++++++++++++++++++++ > sysdeps/pthread/tst-create1mod.c | 41 +++++++++++ > 3 files changed, 169 insertions(+), 2 deletions(-) > create mode 100644 sysdeps/pthread/tst-create1.c > create mode 100644 sysdeps/pthread/tst-create1mod.c > > diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile > index 48dba717a1..4a21abdc38 100644 > --- a/sysdeps/pthread/Makefile > +++ b/sysdeps/pthread/Makefile > @@ -150,15 +150,17 @@ tests += tst-cancelx2 tst-cancelx3 tst-cancelx6 tst-cancelx8 tst-cancelx9 \ > tst-cleanupx0 tst-cleanupx1 tst-cleanupx2 tst-cleanupx3 > > ifeq ($(build-shared),yes) > -tests += tst-atfork2 tst-pt-tls4 tst-_res1 tst-fini1 > +tests += tst-atfork2 tst-pt-tls4 tst-_res1 tst-fini1 tst-create1 > tests-nolibpthread += tst-fini1 > endif > > modules-names += tst-atfork2mod tst-tls4moda tst-tls4modb \ > - tst-_res1mod1 tst-_res1mod2 tst-fini1mod > + tst-_res1mod1 tst-_res1mod2 tst-fini1mod \ > + tst-create1mod > test-modules = $(addprefix $(objpfx),$(addsuffix .so,$(modules-names))) > > tst-atfork2mod.so-no-z-defs = yes > +tst-create1mod.so-no-z-defs = yes > > ifeq ($(build-shared),yes) > # Build all the modules even when not actually running test programs. > @@ -277,4 +279,8 @@ LDFLAGS-tst-join7mod.so = -Wl,-soname,tst-join7mod.so > > CFLAGS-tst-unwind-thread.c += -funwind-tables > > +LDFLAGS-tst-create1 = -Wl,-export-dynamic > +$(objpfx)tst-create1: $(shared-thread-library) > +$(objpfx)tst-create1.out: $(objpfx)tst-create1mod.so > + > endif Ok. > diff --git a/sysdeps/pthread/tst-create1.c b/sysdeps/pthread/tst-create1.c > new file mode 100644 > index 0000000000..44d1c0fc34 > --- /dev/null > +++ b/sysdeps/pthread/tst-create1.c > @@ -0,0 +1,120 @@ > +/* Verify that pthread_create does not deadlock when ctors take locks. > + Copyright (C) 2021 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 > + <https://www.gnu.org/licenses/>. */ > + > +#include <stdio.h> > +#include <support/xdlfcn.h> > +#include <support/xthread.h> > + > +/* > +Check if ctor and pthread_create deadlocks in > + > +thread 1: dlopen -> ctor -> lock(user_lock) > +thread 2: lock(user_lock) -> pthread_create > + > +or in > + > +thread 1: dlclose -> dtor -> lock(user_lock) > +thread 2: lock(user_lock) -> pthread_create > +*/ > + > +static pthread_barrier_t bar_ctor; > +static pthread_barrier_t bar_dtor; > +static pthread_mutex_t user_lock = PTHREAD_MUTEX_INITIALIZER; > + > +void > +ctor (void) > +{ > + xpthread_barrier_wait (&bar_ctor); > + dprintf (1, "thread 1: in ctor: started.\n"); May use STDOUT_FILENO instead? > + xpthread_mutex_lock (&user_lock); > + dprintf (1, "thread 1: in ctor: locked user_lock.\n"); > + xpthread_mutex_unlock (&user_lock); > + dprintf (1, "thread 1: in ctor: unlocked user_lock.\n"); > + dprintf (1, "thread 1: in ctor: done.\n"); > +} > + > +void > +dtor (void) > +{ > + xpthread_barrier_wait (&bar_dtor); > + dprintf (1, "thread 1: in dtor: started.\n"); > + xpthread_mutex_lock (&user_lock); > + dprintf (1, "thread 1: in dtor: locked user_lock.\n"); > + xpthread_mutex_unlock (&user_lock); > + dprintf (1, "thread 1: in dtor: unlocked user_lock.\n"); > + dprintf (1, "thread 1: in dtor: done.\n"); > +} > + > +static void * > +thread3 (void *a) > +{ > + dprintf (1, "thread 3: started.\n"); > + dprintf (1, "thread 3: done.\n"); > + return 0; > +} > + > +static void * > +thread2 (void *a) > +{ > + pthread_t t3; > + dprintf (1, "thread 2: started.\n"); > + > + xpthread_mutex_lock (&user_lock); > + dprintf (1, "thread 2: locked user_lock.\n"); > + xpthread_barrier_wait (&bar_ctor); > + t3 = xpthread_create (0, thread3, 0); > + xpthread_mutex_unlock (&user_lock); > + dprintf (1, "thread 2: unlocked user_lock.\n"); > + xpthread_join (t3); > + > + xpthread_mutex_lock (&user_lock); > + dprintf (1, "thread 2: locked user_lock.\n"); > + xpthread_barrier_wait (&bar_dtor); > + t3 = xpthread_create (0, thread3, 0); > + xpthread_mutex_unlock (&user_lock); > + dprintf (1, "thread 2: unlocked user_lock.\n"); > + xpthread_join (t3); > + > + dprintf (1, "thread 2: done.\n"); > + return 0; > +} > + > +static void > +thread1 (void) > +{ > + dprintf (1, "thread 1: started.\n"); > + xpthread_barrier_init (&bar_ctor, NULL, 2); > + xpthread_barrier_init (&bar_dtor, NULL, 2); > + pthread_t t2 = xpthread_create (0, thread2, 0); > + void *p = xdlopen ("tst-create1mod.so", RTLD_NOW | RTLD_GLOBAL); > + dprintf (1, "thread 1: dlopen done.\n"); > + xdlclose (p); > + dprintf (1, "thread 1: dlclose done.\n"); > + xpthread_join (t2); > + dprintf (1, "thread 1: done.\n"); > +} > + > +int Use static here as well. > +do_test (void) > +{ > + thread1 (); > + return 0; > +} > + > +#define TEST_FUNCTION do_test () > +#include "../test-skeleton.c" Use #include <support/test-driver.c>. > diff --git a/sysdeps/pthread/tst-create1mod.c b/sysdeps/pthread/tst-create1mod.c > new file mode 100644 > index 0000000000..62c9006961 > --- /dev/null > +++ b/sysdeps/pthread/tst-create1mod.c > @@ -0,0 +1,41 @@ > +/* Verify that pthread_create does not deadlock when ctors take locks. > + Copyright (C) 2021 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 > + <https://www.gnu.org/licenses/>. */ > + > +#include <stdio.h> > + > +/* Require TLS setup for the module. */ > +__thread int tlsvar; > + > +void ctor (void); > +void dtor (void); > + > +static void __attribute__ ((constructor)) > +do_init (void) > +{ > + dprintf (1, "constructor started: %d.\n", tlsvar++); > + ctor (); > + dprintf (1, "constructor done: %d.\n", tlsvar++); > +} > + > +static void __attribute__ ((destructor)) > +do_end (void) > +{ > + dprintf (1, "destructor started: %d.\n", tlsvar++); > + dtor (); > + dprintf (1, "destructor done: %d.\n", tlsvar++); > +} >
diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile index 48dba717a1..4a21abdc38 100644 --- a/sysdeps/pthread/Makefile +++ b/sysdeps/pthread/Makefile @@ -150,15 +150,17 @@ tests += tst-cancelx2 tst-cancelx3 tst-cancelx6 tst-cancelx8 tst-cancelx9 \ tst-cleanupx0 tst-cleanupx1 tst-cleanupx2 tst-cleanupx3 ifeq ($(build-shared),yes) -tests += tst-atfork2 tst-pt-tls4 tst-_res1 tst-fini1 +tests += tst-atfork2 tst-pt-tls4 tst-_res1 tst-fini1 tst-create1 tests-nolibpthread += tst-fini1 endif modules-names += tst-atfork2mod tst-tls4moda tst-tls4modb \ - tst-_res1mod1 tst-_res1mod2 tst-fini1mod + tst-_res1mod1 tst-_res1mod2 tst-fini1mod \ + tst-create1mod test-modules = $(addprefix $(objpfx),$(addsuffix .so,$(modules-names))) tst-atfork2mod.so-no-z-defs = yes +tst-create1mod.so-no-z-defs = yes ifeq ($(build-shared),yes) # Build all the modules even when not actually running test programs. @@ -277,4 +279,8 @@ LDFLAGS-tst-join7mod.so = -Wl,-soname,tst-join7mod.so CFLAGS-tst-unwind-thread.c += -funwind-tables +LDFLAGS-tst-create1 = -Wl,-export-dynamic +$(objpfx)tst-create1: $(shared-thread-library) +$(objpfx)tst-create1.out: $(objpfx)tst-create1mod.so + endif diff --git a/sysdeps/pthread/tst-create1.c b/sysdeps/pthread/tst-create1.c new file mode 100644 index 0000000000..44d1c0fc34 --- /dev/null +++ b/sysdeps/pthread/tst-create1.c @@ -0,0 +1,120 @@ +/* Verify that pthread_create does not deadlock when ctors take locks. + Copyright (C) 2021 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 + <https://www.gnu.org/licenses/>. */ + +#include <stdio.h> +#include <support/xdlfcn.h> +#include <support/xthread.h> + +/* +Check if ctor and pthread_create deadlocks in + +thread 1: dlopen -> ctor -> lock(user_lock) +thread 2: lock(user_lock) -> pthread_create + +or in + +thread 1: dlclose -> dtor -> lock(user_lock) +thread 2: lock(user_lock) -> pthread_create +*/ + +static pthread_barrier_t bar_ctor; +static pthread_barrier_t bar_dtor; +static pthread_mutex_t user_lock = PTHREAD_MUTEX_INITIALIZER; + +void +ctor (void) +{ + xpthread_barrier_wait (&bar_ctor); + dprintf (1, "thread 1: in ctor: started.\n"); + xpthread_mutex_lock (&user_lock); + dprintf (1, "thread 1: in ctor: locked user_lock.\n"); + xpthread_mutex_unlock (&user_lock); + dprintf (1, "thread 1: in ctor: unlocked user_lock.\n"); + dprintf (1, "thread 1: in ctor: done.\n"); +} + +void +dtor (void) +{ + xpthread_barrier_wait (&bar_dtor); + dprintf (1, "thread 1: in dtor: started.\n"); + xpthread_mutex_lock (&user_lock); + dprintf (1, "thread 1: in dtor: locked user_lock.\n"); + xpthread_mutex_unlock (&user_lock); + dprintf (1, "thread 1: in dtor: unlocked user_lock.\n"); + dprintf (1, "thread 1: in dtor: done.\n"); +} + +static void * +thread3 (void *a) +{ + dprintf (1, "thread 3: started.\n"); + dprintf (1, "thread 3: done.\n"); + return 0; +} + +static void * +thread2 (void *a) +{ + pthread_t t3; + dprintf (1, "thread 2: started.\n"); + + xpthread_mutex_lock (&user_lock); + dprintf (1, "thread 2: locked user_lock.\n"); + xpthread_barrier_wait (&bar_ctor); + t3 = xpthread_create (0, thread3, 0); + xpthread_mutex_unlock (&user_lock); + dprintf (1, "thread 2: unlocked user_lock.\n"); + xpthread_join (t3); + + xpthread_mutex_lock (&user_lock); + dprintf (1, "thread 2: locked user_lock.\n"); + xpthread_barrier_wait (&bar_dtor); + t3 = xpthread_create (0, thread3, 0); + xpthread_mutex_unlock (&user_lock); + dprintf (1, "thread 2: unlocked user_lock.\n"); + xpthread_join (t3); + + dprintf (1, "thread 2: done.\n"); + return 0; +} + +static void +thread1 (void) +{ + dprintf (1, "thread 1: started.\n"); + xpthread_barrier_init (&bar_ctor, NULL, 2); + xpthread_barrier_init (&bar_dtor, NULL, 2); + pthread_t t2 = xpthread_create (0, thread2, 0); + void *p = xdlopen ("tst-create1mod.so", RTLD_NOW | RTLD_GLOBAL); + dprintf (1, "thread 1: dlopen done.\n"); + xdlclose (p); + dprintf (1, "thread 1: dlclose done.\n"); + xpthread_join (t2); + dprintf (1, "thread 1: done.\n"); +} + +int +do_test (void) +{ + thread1 (); + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/sysdeps/pthread/tst-create1mod.c b/sysdeps/pthread/tst-create1mod.c new file mode 100644 index 0000000000..62c9006961 --- /dev/null +++ b/sysdeps/pthread/tst-create1mod.c @@ -0,0 +1,41 @@ +/* Verify that pthread_create does not deadlock when ctors take locks. + Copyright (C) 2021 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 + <https://www.gnu.org/licenses/>. */ + +#include <stdio.h> + +/* Require TLS setup for the module. */ +__thread int tlsvar; + +void ctor (void); +void dtor (void); + +static void __attribute__ ((constructor)) +do_init (void) +{ + dprintf (1, "constructor started: %d.\n", tlsvar++); + ctor (); + dprintf (1, "constructor done: %d.\n", tlsvar++); +} + +static void __attribute__ ((destructor)) +do_end (void) +{ + dprintf (1, "destructor started: %d.\n", tlsvar++); + dtor (); + dprintf (1, "destructor done: %d.\n", tlsvar++); +}