c5d972
commit 254d3d5aef2fd8430c469e1938209ac100ebf132
c5d972
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
c5d972
Date:   Mon Jan 24 10:46:16 2022 -0300
c5d972
c5d972
    elf: Fix initial-exec TLS access on audit modules (BZ #28096)
c5d972
    
c5d972
    For audit modules and dependencies with initial-exec TLS, we can not
c5d972
    set the initial TLS image on default loader initialization because it
c5d972
    would already be set by the audit setup.  However, subsequent thread
c5d972
    creation would need to follow the default behaviour.
c5d972
    
c5d972
    This patch fixes it by setting l_auditing link_map field not only
c5d972
    for the audit modules, but also for all its dependencies.  This is
c5d972
    used on _dl_allocate_tls_init to avoid the static TLS initialization
c5d972
    at load time.
c5d972
    
c5d972
    Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
c5d972
    
c5d972
    Reviewed-by: Carlos O'Donell <carlos@redhat.com>
c5d972
    Tested-by: Carlos O'Donell <carlos@redhat.com>
c5d972
c5d972
diff --git a/elf/Makefile b/elf/Makefile
c5d972
index e4955c9f575f9015..3f5f72257a5fbea4 100644
c5d972
--- a/elf/Makefile
c5d972
+++ b/elf/Makefile
c5d972
@@ -344,6 +344,7 @@ tests += \
c5d972
   tst-audit19b \
c5d972
   tst-audit2 \
c5d972
   tst-audit20 \
c5d972
+  tst-audit21 \
c5d972
   tst-audit22 \
c5d972
   tst-audit23 \
c5d972
   tst-audit8 \
c5d972
@@ -631,6 +632,8 @@ modules-names = \
c5d972
   tst-auditmod19a \
c5d972
   tst-auditmod19b \
c5d972
   tst-auditmod20 \
c5d972
+  tst-auditmod21a \
c5d972
+  tst-auditmod21b \
c5d972
   tst-auditmod22 \
c5d972
   tst-auditmod23 \
c5d972
   tst-big-note-lib \
c5d972
@@ -2041,6 +2044,11 @@ $(objpfx)tst-audit20.out: $(objpfx)tst-auditmod20.so
c5d972
 tst-audit20-ENV = LD_AUDIT=$(objpfx)tst-auditmod20.so
c5d972
 $(objpfx)tst-auditmod20.so: $(libdl)
c5d972
 
c5d972
+$(objpfx)tst-audit21: $(shared-thread-library)
c5d972
+$(objpfx)tst-audit21.out: $(objpfx)tst-auditmod21a.so
c5d972
+$(objpfx)tst-auditmod21a.so: $(objpfx)tst-auditmod21b.so
c5d972
+tst-audit21-ENV = LD_AUDIT=$(objpfx)tst-auditmod21a.so
c5d972
+
c5d972
 $(objpfx)tst-audit22.out: $(objpfx)tst-auditmod22.so
c5d972
 tst-audit22-ARGS = -- $(host-test-program-cmd)
c5d972
 
c5d972
diff --git a/elf/dl-tls.c b/elf/dl-tls.c
c5d972
index 7865fc390c3f3f0a..a918e9a6f585eb72 100644
c5d972
--- a/elf/dl-tls.c
c5d972
+++ b/elf/dl-tls.c
c5d972
@@ -514,8 +514,12 @@ _dl_resize_dtv (dtv_t *dtv, size_t max_modid)
c5d972
 }
c5d972
 
c5d972
 
c5d972
+/* Allocate initial TLS.  RESULT should be a non-NULL pointer to storage
c5d972
+   for the TLS space.  The DTV may be resized, and so this function may
c5d972
+   call malloc to allocate that space.  The loader's GL(dl_load_tls_lock)
c5d972
+   is taken when manipulating global TLS-related data in the loader.  */
c5d972
 void *
c5d972
-_dl_allocate_tls_init (void *result)
c5d972
+_dl_allocate_tls_init (void *result, bool init_tls)
c5d972
 {
c5d972
   if (result == NULL)
c5d972
     /* The memory allocation failed.  */
c5d972
@@ -588,7 +592,14 @@ _dl_allocate_tls_init (void *result)
c5d972
 	     some platforms use in static programs requires it.  */
c5d972
 	  dtv[map->l_tls_modid].pointer.val = dest;
c5d972
 
c5d972
-	  /* Copy the initialization image and clear the BSS part.  */
c5d972
+	  /* Copy the initialization image and clear the BSS part.  For
c5d972
+	     audit modules or dependencies with initial-exec TLS, we can not
c5d972
+	     set the initial TLS image on default loader initialization
c5d972
+	     because it would already be set by the audit setup.  However,
c5d972
+	     subsequent thread creation would need to follow the default
c5d972
+	     behaviour.   */
c5d972
+	  if (map->l_ns != LM_ID_BASE && !init_tls)
c5d972
+	    continue;
c5d972
 	  memset (__mempcpy (dest, map->l_tls_initimage,
c5d972
 			     map->l_tls_initimage_size), '\0',
c5d972
 		  map->l_tls_blocksize - map->l_tls_initimage_size);
c5d972
@@ -615,7 +626,7 @@ _dl_allocate_tls (void *mem)
c5d972
 {
c5d972
   return _dl_allocate_tls_init (mem == NULL
c5d972
 				? _dl_allocate_tls_storage ()
c5d972
-				: allocate_dtv (mem));
c5d972
+				: allocate_dtv (mem), true);
c5d972
 }
c5d972
 rtld_hidden_def (_dl_allocate_tls)
c5d972
 
c5d972
diff --git a/elf/rtld.c b/elf/rtld.c
c5d972
index efcbeac6c24c4b7b..caa980dbda3d1a72 100644
c5d972
--- a/elf/rtld.c
c5d972
+++ b/elf/rtld.c
c5d972
@@ -2421,7 +2421,7 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
c5d972
      into the main thread's TLS area, which we allocated above.
c5d972
      Note: thread-local variables must only be accessed after completing
c5d972
      the next step.  */
c5d972
-  _dl_allocate_tls_init (tcbp);
c5d972
+  _dl_allocate_tls_init (tcbp, false);
c5d972
 
c5d972
   /* And finally install it for the main thread.  */
c5d972
   if (! tls_init_tp_called)
c5d972
diff --git a/elf/tst-audit21.c b/elf/tst-audit21.c
c5d972
new file mode 100644
c5d972
index 0000000000000000..3a47ab64d44421ee
c5d972
--- /dev/null
c5d972
+++ b/elf/tst-audit21.c
c5d972
@@ -0,0 +1,42 @@
c5d972
+/* Check LD_AUDIT with static TLS.
c5d972
+   Copyright (C) 2022 Free Software Foundation, Inc.
c5d972
+   This file is part of the GNU C Library.
c5d972
+
c5d972
+   The GNU C Library is free software; you can redistribute it and/or
c5d972
+   modify it under the terms of the GNU Lesser General Public
c5d972
+   License as published by the Free Software Foundation; either
c5d972
+   version 2.1 of the License, or (at your option) any later version.
c5d972
+
c5d972
+   The GNU C Library is distributed in the hope that it will be useful,
c5d972
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
c5d972
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
c5d972
+   Lesser General Public License for more details.
c5d972
+
c5d972
+   You should have received a copy of the GNU Lesser General Public
c5d972
+   License along with the GNU C Library; if not, see
c5d972
+   <https://www.gnu.org/licenses/>.  */
c5d972
+
c5d972
+#include <ctype.h>
c5d972
+#include <support/xthread.h>
c5d972
+#include <support/check.h>
c5d972
+
c5d972
+static volatile __thread int out __attribute__ ((tls_model ("initial-exec")));
c5d972
+
c5d972
+static void *
c5d972
+tf (void *arg)
c5d972
+{
c5d972
+  TEST_COMPARE (out, 0);
c5d972
+  out = isspace (' ');
c5d972
+  return NULL;
c5d972
+}
c5d972
+
c5d972
+int main (int argc, char *argv[])
c5d972
+{
c5d972
+  TEST_COMPARE (out, 0);
c5d972
+  out = isspace (' ');
c5d972
+
c5d972
+  pthread_t t = xpthread_create (NULL, tf, NULL);
c5d972
+  xpthread_join (t);
c5d972
+
c5d972
+  return 0;
c5d972
+}
c5d972
diff --git a/elf/tst-auditmod21a.c b/elf/tst-auditmod21a.c
c5d972
new file mode 100644
c5d972
index 0000000000000000..f6d51b5c0531c49d
c5d972
--- /dev/null
c5d972
+++ b/elf/tst-auditmod21a.c
c5d972
@@ -0,0 +1,80 @@
c5d972
+/* Check LD_AUDIT with static TLS.
c5d972
+   Copyright (C) 2022 Free Software Foundation, Inc.
c5d972
+   This file is part of the GNU C Library.
c5d972
+
c5d972
+   The GNU C Library is free software; you can redistribute it and/or
c5d972
+   modify it under the terms of the GNU Lesser General Public
c5d972
+   License as published by the Free Software Foundation; either
c5d972
+   version 2.1 of the License, or (at your option) any later version.
c5d972
+
c5d972
+   The GNU C Library is distributed in the hope that it will be useful,
c5d972
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
c5d972
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
c5d972
+   Lesser General Public License for more details.
c5d972
+
c5d972
+   You should have received a copy of the GNU Lesser General Public
c5d972
+   License along with the GNU C Library; if not, see
c5d972
+   <https://www.gnu.org/licenses/>.  */
c5d972
+
c5d972
+#include <ctype.h>
c5d972
+#include <stdlib.h>
c5d972
+#include <link.h>
c5d972
+
c5d972
+#define tls_ie __attribute__ ((tls_model ("initial-exec")))
c5d972
+
c5d972
+__thread int tls_var0 tls_ie;
c5d972
+__thread int tls_var1 tls_ie = 0x10;
c5d972
+
c5d972
+/* Defined at tst-auditmod21b.so  */
c5d972
+extern __thread int tls_var2;
c5d972
+extern __thread int tls_var3;
c5d972
+
c5d972
+static volatile int out;
c5d972
+
c5d972
+static void
c5d972
+call_libc (void)
c5d972
+{
c5d972
+  /* isspace accesses the initial-exec glibc TLS variables, which are
c5d972
+     setup in glibc initialization.  */
c5d972
+  out = isspace (' ');
c5d972
+}
c5d972
+
c5d972
+unsigned int
c5d972
+la_version (unsigned int v)
c5d972
+{
c5d972
+  tls_var0 = 0x1;
c5d972
+  if (tls_var1 != 0x10)
c5d972
+    abort ();
c5d972
+  tls_var1 = 0x20;
c5d972
+
c5d972
+  tls_var2 = 0x2;
c5d972
+  if (tls_var3 != 0x20)
c5d972
+    abort ();
c5d972
+  tls_var3 = 0x40;
c5d972
+
c5d972
+  call_libc ();
c5d972
+
c5d972
+  return LAV_CURRENT;
c5d972
+}
c5d972
+
c5d972
+unsigned int
c5d972
+la_objopen (struct link_map* map, Lmid_t lmid, uintptr_t* cookie)
c5d972
+{
c5d972
+  call_libc ();
c5d972
+  *cookie = (uintptr_t) map;
c5d972
+  return 0;
c5d972
+}
c5d972
+
c5d972
+void
c5d972
+la_activity (uintptr_t* cookie, unsigned int flag)
c5d972
+{
c5d972
+  if (tls_var0 != 0x1 || tls_var1 != 0x20)
c5d972
+    abort ();
c5d972
+  call_libc ();
c5d972
+}
c5d972
+
c5d972
+void
c5d972
+la_preinit (uintptr_t* cookie)
c5d972
+{
c5d972
+  call_libc ();
c5d972
+}
c5d972
diff --git a/elf/tst-auditmod21b.c b/elf/tst-auditmod21b.c
c5d972
new file mode 100644
c5d972
index 0000000000000000..6ba5335b7514c674
c5d972
--- /dev/null
c5d972
+++ b/elf/tst-auditmod21b.c
c5d972
@@ -0,0 +1,22 @@
c5d972
+/* Check LD_AUDIT with static TLS.
c5d972
+   Copyright (C) 2022 Free Software Foundation, Inc.
c5d972
+   This file is part of the GNU C Library.
c5d972
+
c5d972
+   The GNU C Library is free software; you can redistribute it and/or
c5d972
+   modify it under the terms of the GNU Lesser General Public
c5d972
+   License as published by the Free Software Foundation; either
c5d972
+   version 2.1 of the License, or (at your option) any later version.
c5d972
+
c5d972
+   The GNU C Library is distributed in the hope that it will be useful,
c5d972
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
c5d972
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
c5d972
+   Lesser General Public License for more details.
c5d972
+
c5d972
+   You should have received a copy of the GNU Lesser General Public
c5d972
+   License along with the GNU C Library; if not, see
c5d972
+   <https://www.gnu.org/licenses/>.  */
c5d972
+
c5d972
+#define tls_ie __attribute__ ((tls_model ("initial-exec")))
c5d972
+
c5d972
+__thread int tls_var2 tls_ie;
c5d972
+__thread int tls_var3 tls_ie = 0x20;
c5d972
diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
c5d972
index 5fa45b19987717e1..58170d9da2bf0fa6 100644
c5d972
--- a/nptl/allocatestack.c
c5d972
+++ b/nptl/allocatestack.c
c5d972
@@ -244,7 +244,7 @@ get_cached_stack (size_t *sizep, void **memp)
c5d972
   memset (dtv, '\0', (dtv[-1].counter + 1) * sizeof (dtv_t));
c5d972
 
c5d972
   /* Re-initialize the TLS.  */
c5d972
-  _dl_allocate_tls_init (TLS_TPADJ (result));
c5d972
+  _dl_allocate_tls_init (TLS_TPADJ (result), true);
c5d972
 
c5d972
   return result;
c5d972
 }
c5d972
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
c5d972
index 29b77b35175c1116..73f4863fd43922b9 100644
c5d972
--- a/sysdeps/generic/ldsodefs.h
c5d972
+++ b/sysdeps/generic/ldsodefs.h
c5d972
@@ -1182,7 +1182,7 @@ extern void _dl_allocate_static_tls (struct link_map *map) attribute_hidden;
c5d972
 /* These are internal entry points to the two halves of _dl_allocate_tls,
c5d972
    only used within rtld.c itself at startup time.  */
c5d972
 extern void *_dl_allocate_tls_storage (void) attribute_hidden;
c5d972
-extern void *_dl_allocate_tls_init (void *);
c5d972
+extern void *_dl_allocate_tls_init (void *, bool);
c5d972
 rtld_hidden_proto (_dl_allocate_tls_init)
c5d972
 
c5d972
 /* Deallocate memory allocated with _dl_allocate_tls.  */