199 lines
6.2 KiB
Diff
199 lines
6.2 KiB
Diff
commit 980450f12685326729d63ff72e93a996113bf073
|
|
Author: Szabolcs Nagy <szabolcs.nagy@arm.com>
|
|
Date: Wed Nov 29 11:31:37 2023 +0000
|
|
|
|
elf: Add TLS modid reuse test for bug 29039
|
|
|
|
This is a minimal regression test for bug 29039 which only affects
|
|
targets with TLSDESC and a reproducer requires that
|
|
|
|
1) Have modid gaps (closed modules) with old generation.
|
|
2) Update a DTV to a newer generation (needs a newer dlopen).
|
|
3) But do not update the closed gap entry in that DTV.
|
|
4) Reuse the modid gap for a new module (another dlopen).
|
|
5) Use dynamic TLSDESC in that new module with old generation (bug).
|
|
6) Access TLS via this TLSDESC and the now outdated DTV.
|
|
|
|
However step (3) in practice rarely happens: during DTV update the
|
|
entries for closed modids are initialized to "unallocated" and then
|
|
dynamic TLSDESC calls __tls_get_addr independently of its generation.
|
|
The only exception to this is DTV setup at thread creation (gaps are
|
|
initialized to NULL instead of unallocated) or DTV resize where the
|
|
gap entries are outside the previous DTV array (again NULL instead
|
|
of unallocated, and this requires loading > DTV_SURPLUS modules).
|
|
|
|
So the bug can only cause NULL (+ offset) dereference, not use after
|
|
free. And the easiest way to get (3) is via thread creation.
|
|
|
|
Note that step (5) requires that the newly loaded module has larger
|
|
TLS than the remaining optional static TLS. And for (6) there cannot
|
|
be other TLS access or dlopen in the thread that updates the DTV.
|
|
|
|
Tested on aarch64-linux-gnu.
|
|
|
|
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
|
|
|
Conflicts:
|
|
elf/Makefile
|
|
(Add $(libdl), Resolve test case ordering conflict.)
|
|
|
|
diff --git a/elf/Makefile b/elf/Makefile
|
|
index ebf46a297d241d8f..b8fdee7c0d37137e 100644
|
|
--- a/elf/Makefile
|
|
+++ b/elf/Makefile
|
|
@@ -416,6 +416,7 @@ tests += \
|
|
tst-tls-ie \
|
|
tst-tls-ie-dlmopen \
|
|
tst-tls-manydynamic \
|
|
+ tst-tlsgap \
|
|
tst-unique1 \
|
|
tst-unique2 \
|
|
unload3 \
|
|
@@ -759,6 +760,9 @@ modules-names = \
|
|
tst-tls20mod-bad \
|
|
tst-tls21mod \
|
|
tst-tlsalign-lib \
|
|
+ tst-tlsgap-mod0 \
|
|
+ tst-tlsgap-mod1 \
|
|
+ tst-tlsgap-mod2 \
|
|
tst-tls-ie-mod0 \
|
|
tst-tls-ie-mod1 \
|
|
tst-tls-ie-mod2 \
|
|
@@ -2731,3 +2735,14 @@ $(objpfx)tst-nodeps2-mod.so: $(common-objpfx)libc.so \
|
|
$(objpfx)tst-nodeps2: $(libdl)
|
|
$(objpfx)tst-nodeps2.out: \
|
|
$(objpfx)tst-nodeps1-mod.so $(objpfx)tst-nodeps2-mod.so
|
|
+
|
|
+$(objpfx)tst-tlsgap: $(libdl) $(shared-thread-library)
|
|
+$(objpfx)tst-tlsgap.out: \
|
|
+ $(objpfx)tst-tlsgap-mod0.so \
|
|
+ $(objpfx)tst-tlsgap-mod1.so \
|
|
+ $(objpfx)tst-tlsgap-mod2.so
|
|
+ifeq (yes,$(have-mtls-dialect-gnu2))
|
|
+CFLAGS-tst-tlsgap-mod0.c += -mtls-dialect=gnu2
|
|
+CFLAGS-tst-tlsgap-mod1.c += -mtls-dialect=gnu2
|
|
+CFLAGS-tst-tlsgap-mod2.c += -mtls-dialect=gnu2
|
|
+endif
|
|
diff --git a/elf/tst-tlsgap-mod0.c b/elf/tst-tlsgap-mod0.c
|
|
new file mode 100644
|
|
index 0000000000000000..1478b0beac5faf98
|
|
--- /dev/null
|
|
+++ b/elf/tst-tlsgap-mod0.c
|
|
@@ -0,0 +1,2 @@
|
|
+int __thread tls0;
|
|
+int *f0(void) { return &tls0; }
|
|
diff --git a/elf/tst-tlsgap-mod1.c b/elf/tst-tlsgap-mod1.c
|
|
new file mode 100644
|
|
index 0000000000000000..b10fc3702c43e478
|
|
--- /dev/null
|
|
+++ b/elf/tst-tlsgap-mod1.c
|
|
@@ -0,0 +1,2 @@
|
|
+int __thread tls1[100]; /* Size > glibc.rtld.optional_static_tls / 2. */
|
|
+int *f1(void) { return tls1; }
|
|
diff --git a/elf/tst-tlsgap-mod2.c b/elf/tst-tlsgap-mod2.c
|
|
new file mode 100644
|
|
index 0000000000000000..166c27d7f3fac252
|
|
--- /dev/null
|
|
+++ b/elf/tst-tlsgap-mod2.c
|
|
@@ -0,0 +1,2 @@
|
|
+int __thread tls2;
|
|
+int *f2(void) { return &tls2; }
|
|
diff --git a/elf/tst-tlsgap.c b/elf/tst-tlsgap.c
|
|
new file mode 100644
|
|
index 0000000000000000..49328850769c5609
|
|
--- /dev/null
|
|
+++ b/elf/tst-tlsgap.c
|
|
@@ -0,0 +1,92 @@
|
|
+/* TLS modid gap reuse regression test for bug 29039.
|
|
+ Copyright (C) 2023 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
|
|
+ <http://www.gnu.org/licenses/>. */
|
|
+
|
|
+#include <stdio.h>
|
|
+#include <dlfcn.h>
|
|
+#include <pthread.h>
|
|
+#include <support/xdlfcn.h>
|
|
+#include <support/xthread.h>
|
|
+#include <support/check.h>
|
|
+
|
|
+static void *mod[3];
|
|
+#define MOD(i) "tst-tlsgap-mod" #i ".so"
|
|
+static const char *modname[3] = { MOD(0), MOD(1), MOD(2) };
|
|
+#undef MOD
|
|
+
|
|
+static void
|
|
+open_mod (int i)
|
|
+{
|
|
+ mod[i] = xdlopen (modname[i], RTLD_LAZY);
|
|
+ printf ("open %s\n", modname[i]);
|
|
+}
|
|
+
|
|
+static void
|
|
+close_mod (int i)
|
|
+{
|
|
+ xdlclose (mod[i]);
|
|
+ mod[i] = NULL;
|
|
+ printf ("close %s\n", modname[i]);
|
|
+}
|
|
+
|
|
+static void
|
|
+access_mod (int i, const char *sym)
|
|
+{
|
|
+ int *(*f) (void) = xdlsym (mod[i], sym);
|
|
+ int *p = f ();
|
|
+ printf ("access %s: %s() = %p\n", modname[i], sym, p);
|
|
+ TEST_VERIFY_EXIT (p != NULL);
|
|
+ ++*p;
|
|
+}
|
|
+
|
|
+static void *
|
|
+start (void *arg)
|
|
+{
|
|
+ /* The DTV generation is at the last dlopen of mod0 and the
|
|
+ entry for mod1 is NULL. */
|
|
+
|
|
+ open_mod (1); /* Reuse modid of mod1. Uses dynamic TLS. */
|
|
+
|
|
+ /* DTV is unchanged: dlopen only updates the DTV to the latest
|
|
+ generation if static TLS is allocated for a loaded module.
|
|
+
|
|
+ With bug 29039, the TLSDESC relocation in mod1 uses the old
|
|
+ dlclose generation of mod1 instead of the new dlopen one so
|
|
+ DTV is not updated on TLS access. */
|
|
+
|
|
+ access_mod (1, "f1");
|
|
+
|
|
+ return arg;
|
|
+}
|
|
+
|
|
+static int
|
|
+do_test (void)
|
|
+{
|
|
+ open_mod (0);
|
|
+ open_mod (1);
|
|
+ open_mod (2);
|
|
+ close_mod (0);
|
|
+ close_mod (1); /* Create modid gap at mod1. */
|
|
+ open_mod (0); /* Reuse modid of mod0, bump generation count. */
|
|
+
|
|
+ /* Create a thread where DTV of mod1 is NULL. */
|
|
+ pthread_t t = xpthread_create (NULL, start, NULL);
|
|
+ xpthread_join (t);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#include <support/test-driver.c>
|