summaryrefslogtreecommitdiff
path: root/backport-elf-Add-TLS-modid-reuse-test-for-bug-29039.patch
diff options
context:
space:
mode:
Diffstat (limited to 'backport-elf-Add-TLS-modid-reuse-test-for-bug-29039.patch')
-rw-r--r--backport-elf-Add-TLS-modid-reuse-test-for-bug-29039.patch209
1 files changed, 209 insertions, 0 deletions
diff --git a/backport-elf-Add-TLS-modid-reuse-test-for-bug-29039.patch b/backport-elf-Add-TLS-modid-reuse-test-for-bug-29039.patch
new file mode 100644
index 0000000..03efbca
--- /dev/null
+++ b/backport-elf-Add-TLS-modid-reuse-test-for-bug-29039.patch
@@ -0,0 +1,209 @@
+From 980450f12685326729d63ff72e93a996113bf073 Mon Sep 17 00:00:00 2001
+From: Szabolcs Nagy <szabolcs.nagy@arm.com>
+Date: Wed, 29 Nov 2023 11:31:37 +0000
+Subject: [PATCH] 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.
+
+Reference:https://sourceware.org/git/?p=glibc.git;a=commit;h=980450f12685326729d63ff72e93a996113bf073
+
+Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
+---
+ elf/Makefile | 15 +++++++
+ elf/tst-tlsgap-mod0.c | 2 +
+ elf/tst-tlsgap-mod1.c | 2 +
+ elf/tst-tlsgap-mod2.c | 2 +
+ elf/tst-tlsgap.c | 92 +++++++++++++++++++++++++++++++++++++++++++
+ 5 files changed, 113 insertions(+)
+ create mode 100644 elf/tst-tlsgap-mod0.c
+ create mode 100644 elf/tst-tlsgap-mod1.c
+ create mode 100644 elf/tst-tlsgap-mod2.c
+ create mode 100644 elf/tst-tlsgap.c
+
+diff --git a/elf/Makefile b/elf/Makefile
+index ed13f34d..b5de4dd4 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -425,6 +425,7 @@ tests += \
+ tst-tls5 \
+ tst-tlsalign \
+ tst-tlsalign-extern \
++ tst-tlsgap \
+ tst-tls-dlinfo \
+ tst-tls-ie \
+ tst-tls-ie-dlmopen \
+@@ -704,6 +705,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 \
+@@ -2498,3 +2502,14 @@ $(objpfx)tst-non-directory-path.out: tst-non-directory-path.sh \
+ '$(test-wrapper-env)' '$(run_program_env)' \
+ '$(rpath-link)' $(objpfx) > $@; \
+ $(evaluate-test)
++
++$(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 00000000..1478b0be
+--- /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 00000000..b10fc370
+--- /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 00000000..166c27d7
+--- /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 00000000..49328850
+--- /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>
+--
+2.33.0
+