diff mbox series

[2/2] Add a pthread_create deadlock test [BZ #28357]

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

Commit Message

Szabolcs Nagy Sept. 22, 2021, 7:51 a.m. UTC
Check if locks in ctors or dtors can cause pthread_create to deadlock.
---
 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

Comments

Adhemerval Zanella Netto Oct. 1, 2021, 11:41 a.m. UTC | #1
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 mbox series

Patch

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++);
+}