about summary refs log tree commit diff
diff options
context:
space:
mode:
authorStan Shebs <stanshebs@google.com>2016-04-27 09:53:33 -0700
committerStan Shebs <stanshebs@google.com>2016-04-27 09:53:33 -0700
commitc93632edca4fc68f5873d53d70d7266833b2b25c (patch)
treecebd045d8a886831db982cc0a1bc6032357278bd
parent000f2cf415b761b4c990b067276f3bb209cc1f95 (diff)
downloadglibc-c93632edca4fc68f5873d53d70d7266833b2b25c.tar.gz
glibc-c93632edca4fc68f5873d53d70d7266833b2b25c.tar.xz
glibc-c93632edca4fc68f5873d53d70d7266833b2b25c.zip
Fix infinite loop on process exit.
-rw-r--r--README.google4
-rw-r--r--nptl/sysdeps/unix/sysv/linux/register-atfork.c30
2 files changed, 34 insertions, 0 deletions
diff --git a/README.google b/README.google
index 1b5b4b76b8..85b310ab95 100644
--- a/README.google
+++ b/README.google
@@ -566,3 +566,7 @@ sysdeps/powerpc/*
 sysdeps/powerpc/bits/fenvinline.h
   For b/27191207, remove use of %s modifier in inline asm.
   (stanshebs, google-local)
+
+nptl/sysdeps/unix/sysv/linux/register-atfork.c
+  For b/28011264, detect and work around loop in fork handler list.
+  (stanshebs, google-local)
diff --git a/nptl/sysdeps/unix/sysv/linux/register-atfork.c b/nptl/sysdeps/unix/sysv/linux/register-atfork.c
index 2cc49540b9..bf1deecc41 100644
--- a/nptl/sysdeps/unix/sysv/linux/register-atfork.c
+++ b/nptl/sysdeps/unix/sysv/linux/register-atfork.c
@@ -112,6 +112,36 @@ void
 attribute_hidden
 __linkin_atfork (struct fork_handler *newp)
 {
+  /* GRTE's patches for async-signal-safe TLS can cause a race
+     condition in which ptmalloc_init is called from more than one
+     thread. (allocate_dtv normally calls calloc which invokes
+     ptmalloc_init via hook while creating the first thread, but our
+     code calls __signal_safe_calloc which does not run hooks.)
+     ptmalloc_init tries to be idempotent in case of multiple threads,
+     but in glibc-2.19, it fills in atfork hooks from an
+     un-lock-protected global static atfork_mem, which is a bad idea;
+     it can result in the same allocated object being passed to this
+     routine more than once. This function then sets the object's next
+     pointer to point to itself, resulting in a hang when the program
+     tries to exit.
+
+     This problem has been (indirectly) resolved in upstream glibc by
+     rewriting the whole thing so that thread setup is not done with
+     atforks or static variables, but the changes are extensive and
+     would not backport reliably.  Our race is somewhat difficult to
+     trigger - it requires a program to start creating threads
+     *before* any kind of memory allocation whatsoever.  So given all
+     this, the safest route is simply to detect when the fork handler
+     is already present, and skip adding it altogether.
+
+     Note that while it's conceivable that calls to pthread_atfork
+     would result in the atfork_mem object not being at the head of
+     the list, but testing seems unable to generate such a case.  */
+  struct fork_handler *scanp;
+  for (scanp = __fork_handlers; scanp != NULL; scanp = scanp->next)
+    if (newp == scanp)
+      return;
+
   do
     newp->next = __fork_handlers;
   while (catomic_compare_and_exchange_bool_acq (&__fork_handlers,