diff options
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.patch | 209 |
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 + |