about summary refs log tree commit diff
path: root/elf
diff options
context:
space:
mode:
authorCarlos O'Donell <carlos@redhat.com>2014-02-25 13:00:36 -0500
committerCarlos O'Donell <carlos@redhat.com>2014-02-25 13:18:15 -0500
commitd050367659e04685a0eab910e86ea6829a8d24f9 (patch)
treeeb094a22fb28449c7723b9463c388505ca31a5a3 /elf
parent4cbf380ce948ca15a965a78f0c1a092cf5956792 (diff)
downloadglibc-d050367659e04685a0eab910e86ea6829a8d24f9.tar.gz
glibc-d050367659e04685a0eab910e86ea6829a8d24f9.tar.xz
glibc-d050367659e04685a0eab910e86ea6829a8d24f9.zip
BZ #16613: Support TLS in audit libraries.
This commit fixes a bug where the dynamic loader would crash
when loading audit libraries, via LD_AUDIT, where those libraries
used TLS. The dynamic loader was not considering that the audit
libraries would use TLS and failed to bump the TLS generation
counter leaving TLS usage inconsistent after loading the audit
libraries.

https://sourceware.org/ml/libc-alpha/2014-02/msg00569.html
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile9
-rw-r--r--elf/dl-tls.c28
-rw-r--r--elf/rtld.c7
-rw-r--r--elf/tst-audit9.c8
-rw-r--r--elf/tst-auditmod9a.c15
-rw-r--r--elf/tst-auditmod9b.c6
6 files changed, 71 insertions, 2 deletions
diff --git a/elf/Makefile b/elf/Makefile
index 863acd8627..3b217ce326 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -144,7 +144,7 @@ tests += loadtest restest1 preloadtest loadfail multiload origtest resolvfail \
 	 tst-dlmodcount tst-dlopenrpath tst-deep1 \
 	 tst-dlmopen1 tst-dlmopen2 tst-dlmopen3 \
 	 unload3 unload4 unload5 unload6 unload7 unload8 tst-global1 order2 \
-	 tst-audit1 tst-audit2 tst-audit8 \
+	 tst-audit1 tst-audit2 tst-audit8 tst-audit9 \
 	 tst-stackguard1 tst-addr1 tst-thrlock \
 	 tst-unique1 tst-unique2 tst-unique3 tst-unique4 \
 	 tst-initorder tst-initorder2 tst-relsort1 tst-null-argv \
@@ -203,6 +203,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
 		order2mod1 order2mod2 order2mod3 order2mod4 \
 		tst-unique1mod1 tst-unique1mod2 \
 		tst-unique2mod1 tst-unique2mod2 \
+		tst-auditmod9a tst-auditmod9b \
 		tst-unique3lib tst-unique3lib2 \
 		tst-unique4lib \
 		tst-initordera1 tst-initorderb1 \
@@ -560,6 +561,8 @@ unload4mod1.so-no-z-defs = yes
 ifuncmod1.so-no-z-defs = yes
 ifuncmod5.so-no-z-defs = yes
 ifuncmod6.so-no-z-defs = yes
+tst-auditmod9a.so-no-z-defs = yes
+tst-auditmod9b.so-no-z-defs = yes
 
 ifeq ($(build-shared),yes)
 # Build all the modules even when not actually running test programs.
@@ -1012,6 +1015,10 @@ tst-audit1-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
 $(objpfx)tst-audit2.out: $(objpfx)tst-auditmod1.so
 tst-audit2-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
 
+$(objpfx)tst-audit9: $(libdl)
+$(objpfx)tst-audit9.out: $(objpfx)tst-auditmod9a.so $(objpfx)tst-auditmod9b.so
+tst-audit9-ENV = LD_AUDIT=$(objpfx)tst-auditmod9a.so
+
 $(objpfx)tst-audit8: $(common-objpfx)math/libm.so
 $(objpfx)tst-audit8.out: $(objpfx)tst-auditmod1.so
 tst-audit8-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
diff --git a/elf/dl-tls.c b/elf/dl-tls.c
index 9454d06d72..5204fdaac0 100644
--- a/elf/dl-tls.c
+++ b/elf/dl-tls.c
@@ -105,6 +105,33 @@ _dl_next_tls_modid (void)
 }
 
 
+size_t
+internal_function
+_dl_count_modids (void)
+{
+  /* It is rare that we have gaps; see elf/dl-open.c (_dl_open) where
+     we fail to load a module and unload it leaving a gap.  If we don't
+     have gaps then the number of modids is the current maximum so
+     return that.  */
+  if (__glibc_likely (!GL(dl_tls_dtv_gaps)))
+    return GL(dl_tls_max_dtv_idx);
+
+  /* We have gaps and are forced to count the non-NULL entries.  */
+  size_t n = 0;
+  struct dtv_slotinfo_list *runp = GL(dl_tls_dtv_slotinfo_list);
+  while (runp != NULL)
+    {
+      for (size_t i = 0; i < runp->len; ++i)
+	if (runp->slotinfo[i].map != NULL)
+	  ++n;
+
+      runp = runp->next;
+    }
+
+  return n;
+}
+
+
 #ifdef SHARED
 void
 internal_function
@@ -407,6 +434,7 @@ _dl_allocate_tls_init (void *result)
 
 	  /* Keep track of the maximum generation number.  This might
 	     not be the generation counter.  */
+	  assert (listp->slotinfo[cnt].gen <= GL(dl_tls_generation));
 	  maxgen = MAX (maxgen, listp->slotinfo[cnt].gen);
 
 	  if (map->l_tls_offset == NO_TLS_OFFSET
diff --git a/elf/rtld.c b/elf/rtld.c
index aa50cab0a9..7f1413af08 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -1569,6 +1569,10 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
 	}
     }
 
+  /* Keep track of the currently loaded modules to count how many
+     non-audit modules which use TLS are loaded.  */
+  size_t count_modids = _dl_count_modids ();
+
   /* Set up debugging before the debugger is notified for the first time.  */
 #ifdef ELF_MACHINE_DEBUG_SETUP
   /* Some machines (e.g. MIPS) don't use DT_DEBUG in this way.  */
@@ -2220,7 +2224,8 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
 	_dl_start_profile ();
     }
 
-  if (!was_tls_init_tp_called && GL(dl_tls_max_dtv_idx) > 0)
+  if ((!was_tls_init_tp_called && GL(dl_tls_max_dtv_idx) > 0)
+      || count_modids != _dl_count_modids ())
     ++GL(dl_tls_generation);
 
   /* Now that we have completed relocation, the initializer data
diff --git a/elf/tst-audit9.c b/elf/tst-audit9.c
new file mode 100644
index 0000000000..0982d8b716
--- /dev/null
+++ b/elf/tst-audit9.c
@@ -0,0 +1,8 @@
+#include <dlfcn.h>
+
+int main(void)
+{
+  void *h = dlopen("$ORIGIN/tst-auditmod9b.so", RTLD_LAZY);
+  int (*fp)(void) = dlsym(h, "f");
+  return fp() - 1;
+}
diff --git a/elf/tst-auditmod9a.c b/elf/tst-auditmod9a.c
new file mode 100644
index 0000000000..7213ade123
--- /dev/null
+++ b/elf/tst-auditmod9a.c
@@ -0,0 +1,15 @@
+#include <stdint.h>
+
+__thread int var;
+
+unsigned int
+la_version (unsigned int v)
+{
+  return v;
+}
+
+void
+la_activity (uintptr_t *cookie, unsigned int flag)
+{
+  ++var;
+}
diff --git a/elf/tst-auditmod9b.c b/elf/tst-auditmod9b.c
new file mode 100644
index 0000000000..8eeeb49986
--- /dev/null
+++ b/elf/tst-auditmod9b.c
@@ -0,0 +1,6 @@
+__thread int a;
+
+int f(void)
+{
+  return ++a;
+}