00db10
commit e400f3ccd36fe91d432cc7d45b4ccc799dece763
00db10
Author: Siddhesh Poyarekar <siddhesh@redhat.com>
00db10
Date:   Fri Jul 24 19:13:38 2015 +0530
00db10
00db10
    Use IE model for static variables in libc.so, libpthread.so and rtld
00db10
    
00db10
    The recently introduced TLS variables in the thread-local destructor
00db10
    implementation (__cxa_thread_atexit_impl) used the default GD access
00db10
    model, resulting in a call to __tls_get_addr.  This causes a deadlock
00db10
    with recent changes to the way TLS is initialized because DTV
00db10
    allocations are delayed and hence despite knowing the offset to the
00db10
    variable inside its TLS block, the thread has to take the global rtld
00db10
    lock to safely update the TLS offset.
00db10
    
00db10
    This causes deadlocks when a thread is instantiated and joined inside
00db10
    a destructor of a dlopen'd DSO.  The correct long term fix is to
00db10
    somehow not take the lock, but that will need a lot deeper change set
00db10
    to alter the way in which the big rtld lock is used.
00db10
    
00db10
    Instead, this patch just eliminates the call to __tls_get_addr for the
00db10
    thread-local variables inside libc.so, libpthread.so and rtld by
00db10
    building all of their units with -mtls-model=initial-exec.
00db10
    
00db10
    There were concerns that the static storage for TLS is limited and
00db10
    hence we should not be using it.  Additionally, dynamically loaded
00db10
    modules may result in libc.so looking for this static storage pretty
00db10
    late in static binaries.  Both concerns are valid when using TLSDESC
00db10
    since that is where one may attempt to allocate a TLS block from
00db10
    static storage for even those variables that are not IE.  They're not
00db10
    very strong arguments for the traditional TLS model though, since it
00db10
    assumes that the static storage would be used sparingly and definitely
00db10
    not by default.  Hence, for now this would only theoretically affect
00db10
    ARM architectures.
00db10
    
00db10
    The impact is hence limited to statically linked binaries that dlopen
00db10
    modules that in turn load libc.so, all that on arm hardware.  It seems
00db10
    like a small enough impact to justify fixing the larger problem that
00db10
    currently affects everything everywhere.
00db10
    
00db10
    This still does not solve the original problem completely.  That is,
00db10
    it is still possible to deadlock on the big rtld lock with a small
00db10
    tweak to the test case attached to this patch.  That problem is
00db10
    however not a regression in 2.22 and hence could be tackled as a
00db10
    separate project.  The test case is picked up as is from Alex's patch.
00db10
    
00db10
    This change has been tested to verify that it does not cause any
00db10
    issues on x86_64.
00db10
    
00db10
    ChangeLog:
00db10
    
00db10
    	[BZ #18457]
00db10
    	* nptl/Makefile (tests): New test case tst-join7.
00db10
    	(modules-names): New test case module tst-join7mod.
00db10
    	* nptl/tst-join7.c: New file.
00db10
    	* nptl/tst-join7mod.c: New file.
00db10
    	* Makeconfig (tls-model): Pass -ftls-model=initial-exec for
00db10
    	all translation units in libc.so, libpthread.so and rtld.
00db10
00db10
diff --git a/nptl/Makefile b/nptl/Makefile
00db10
index 140f063..aaca0a4 100644
00db10
--- a/nptl/Makefile
00db10
+++ b/nptl/Makefile
00db10
@@ -245,7 +245,7 @@ tests = tst-typesizes \
00db10
 	tst-basic7 \
00db10
 	tst-kill1 tst-kill2 tst-kill3 tst-kill4 tst-kill5 tst-kill6 \
00db10
 	tst-raise1 \
00db10
-	tst-join1 tst-join2 tst-join3 tst-join4 tst-join5 tst-join6 \
00db10
+	tst-join1 tst-join2 tst-join3 tst-join4 tst-join5 tst-join6 tst-join7 \
00db10
 	tst-detach1 \
00db10
 	tst-eintr1 tst-eintr2 tst-eintr3 tst-eintr4 tst-eintr5 \
00db10
 	tst-tsd1 tst-tsd2 tst-tsd3 tst-tsd4 tst-tsd5 tst-tsd6 \
00db10
@@ -327,7 +327,8 @@ endif
00db10
 modules-names = tst-atfork2mod tst-tls3mod tst-tls4moda tst-tls4modb \
00db10
 		tst-tls5mod tst-tls5moda tst-tls5modb tst-tls5modc \
00db10
 		tst-tls5modd tst-tls5mode tst-tls5modf tst-stack4mod \
00db10
-		tst-_res1mod1 tst-_res1mod2 tst-execstack-mod tst-fini1mod
00db10
+		tst-_res1mod1 tst-_res1mod2 tst-execstack-mod tst-fini1mod \
00db10
+		tst-join7mod
00db10
 extra-test-objs += $(addsuffix .os,$(strip $(modules-names))) tst-cleanup4aux.o
00db10
 test-extras += $(modules-names) tst-cleanup4aux
00db10
 test-modules = $(addprefix $(objpfx),$(addsuffix .so,$(modules-names)))
00db10
@@ -532,6 +533,11 @@ $(objpfx)tst-tls6.out: tst-tls6.sh $(objpfx)tst-tls5 \
00db10
 		    $(rtld-installed-name) '$(test-wrapper-env)'
00db10
 endif
00db10
 
00db10
+$(objpfx)tst-join7: $(libdl) $(shared-thread-library)
00db10
+$(objpfx)tst-join7.out: $(objpfx)tst-join7mod.so
00db10
+$(objpfx)tst-join7mod.so: $(shared-thread-library)
00db10
+LDFLAGS-tst-join7mod.so = -Wl,-soname,tst-join7mod.so
00db10
+
00db10
 $(objpfx)tst-dlsym1: $(libdl) $(shared-thread-library)
00db10
 
00db10
 $(objpfx)tst-fini1: $(shared-thread-library) $(objpfx)tst-fini1mod.so
00db10
diff --git a/nptl/tst-join7.c b/nptl/tst-join7.c
00db10
new file mode 100644
00db10
index 0000000..439d0fc
00db10
--- /dev/null
00db10
+++ b/nptl/tst-join7.c
00db10
@@ -0,0 +1,46 @@
00db10
+/* Verify that TLS access in separate thread in a dlopened library does not
00db10
+   deadlock.
00db10
+   Copyright (C) 2015 Free Software Foundation, Inc.
00db10
+   This file is part of the GNU C Library.
00db10
+
00db10
+   The GNU C Library is free software; you can redistribute it and/or
00db10
+   modify it under the terms of the GNU Lesser General Public
00db10
+   License as published by the Free Software Foundation; either
00db10
+   version 2.1 of the License, or (at your option) any later version.
00db10
+
00db10
+   The GNU C Library is distributed in the hope that it will be useful,
00db10
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
00db10
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00db10
+   Lesser General Public License for more details.
00db10
+
00db10
+   You should have received a copy of the GNU Lesser General Public
00db10
+   License along with the GNU C Library; if not, see
00db10
+   <http://www.gnu.org/licenses/>.  */
00db10
+
00db10
+#include <dlfcn.h>
00db10
+
00db10
+/* When one dynamically loads a module, which spawns a thread to perform some
00db10
+   activities, it could be possible that TLS storage is accessed for the first
00db10
+   time in that thread.  This results in an allocation request within the
00db10
+   thread, which could result in an attempt to take the rtld load_lock.  This
00db10
+   is a problem because it would then deadlock with the dlopen (which owns the
00db10
+   lock), if the main thread is waiting for the spawned thread to exit.  We can
00db10
+   at least ensure that this problem does not occur due to accesses within
00db10
+   libc.so, by marking TLS variables within libc.so as IE.  The problem of an
00db10
+   arbitrary variable being accessed and constructed within such a thread still
00db10
+   exists but this test case does not verify that.  */
00db10
+
00db10
+int
00db10
+do_test (void)
00db10
+{
00db10
+  void *f = dlopen ("tst-join7mod.so", RTLD_NOW | RTLD_GLOBAL);
00db10
+  if (f)
00db10
+    dlclose (f);
00db10
+  else
00db10
+    return 1;
00db10
+
00db10
+  return 0;
00db10
+}
00db10
+
00db10
+#define TEST_FUNCTION do_test ()
00db10
+#include "../test-skeleton.c"
00db10
diff --git a/nptl/tst-join7mod.c b/nptl/tst-join7mod.c
00db10
new file mode 100644
00db10
index 0000000..92bb381
00db10
--- /dev/null
00db10
+++ b/nptl/tst-join7mod.c
00db10
@@ -0,0 +1,61 @@
00db10
+/* Verify that TLS access in separate thread in a dlopened library does not
00db10
+   deadlock - the module.
00db10
+   Copyright (C) 2015 Free Software Foundation, Inc.
00db10
+   This file is part of the GNU C Library.
00db10
+
00db10
+   The GNU C Library is free software; you can redistribute it and/or
00db10
+   modify it under the terms of the GNU Lesser General Public
00db10
+   License as published by the Free Software Foundation; either
00db10
+   version 2.1 of the License, or (at your option) any later version.
00db10
+
00db10
+   The GNU C Library is distributed in the hope that it will be useful,
00db10
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
00db10
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00db10
+   Lesser General Public License for more details.
00db10
+
00db10
+   You should have received a copy of the GNU Lesser General Public
00db10
+   License along with the GNU C Library; if not, see
00db10
+   <http://www.gnu.org/licenses/>.  */
00db10
+
00db10
+#include <stdio.h>
00db10
+#include <pthread.h>
00db10
+#include <atomic.h>
00db10
+
00db10
+static pthread_t th;
00db10
+static int running = 1;
00db10
+
00db10
+static void *
00db10
+test_run (void *p)
00db10
+{
00db10
+  while (atomic_load_relaxed (&running))
00db10
+    printf ("Test running\n");
00db10
+  printf ("Test finished\n");
00db10
+  return NULL;
00db10
+}
00db10
+
00db10
+static void __attribute__ ((constructor))
00db10
+do_init (void)
00db10
+{
00db10
+  int ret = pthread_create (&th, NULL, test_run, NULL);
00db10
+
00db10
+  if (ret != 0)
00db10
+    {
00db10
+      printf ("failed to create thread: %s (%d)\n", strerror (ret), ret);
00db10
+      exit (1);
00db10
+    }
00db10
+}
00db10
+
00db10
+static void __attribute__ ((destructor))
00db10
+do_end (void)
00db10
+{
00db10
+  atomic_store_relaxed (&running, 0);
00db10
+  int ret = pthread_join (th, NULL);
00db10
+
00db10
+  if (ret != 0)
00db10
+    {
00db10
+      printf ("pthread_join: %s(%d)\n", strerror (ret), ret);
00db10
+      exit (1);
00db10
+    }
00db10
+
00db10
+  printf ("Thread joined\n");
00db10
+}
00db10
diff -pruN a/string/strerror_l.c b/string/strerror_l.c
00db10
--- a/string/strerror_l.c
00db10
+++ b/string/strerror_l.c
00db10
@@ -23,7 +23,7 @@
00db10
 #include <sys/param.h>
00db10
 
00db10
 
00db10
-static __thread char *last_value;
00db10
+static __thread char *last_value attribute_tls_model_ie;
00db10
 
00db10
 
00db10
 static const char *