You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
198 lines
6.2 KiB
198 lines
6.2 KiB
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 |
|
(Resolve test case ordering conflict.) |
|
|
|
diff --git a/elf/Makefile b/elf/Makefile |
|
index 7b7c6c171ce23247..7d55e68a55b54bd6 100644 |
|
--- a/elf/Makefile |
|
+++ b/elf/Makefile |
|
@@ -447,6 +447,7 @@ tests += \ |
|
tst-tls5 \ |
|
tst-tlsalign \ |
|
tst-tlsalign-extern \ |
|
+ tst-tlsgap \ |
|
tst-tls-dlinfo \ |
|
tst-tls-ie \ |
|
tst-tls-ie-dlmopen \ |
|
@@ -798,6 +799,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 \ |
|
@@ -2710,3 +2714,14 @@ $(objpfx)tst-nodeps2-mod.so: $(common-objpfx)libc.so \ |
|
$(LINK.o) -Wl,--no-as-needed -nostartfiles -nostdlib -shared -o $@ $^ |
|
$(objpfx)tst-nodeps2.out: \ |
|
$(objpfx)tst-nodeps1-mod.so $(objpfx)tst-nodeps2-mod.so |
|
+ |
|
+$(objpfx)tst-tlsgap: $(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>
|
|
|