]>
Commit | Line | Data |
---|---|---|
a61a21ef MT |
1 | From 0de9082ed8d8f149ca87d569a73692046e236c18 Mon Sep 17 00:00:00 2001 |
2 | From: Szabolcs Nagy <szabolcs.nagy@arm.com> | |
3 | Date: Wed, 29 Nov 2023 11:31:37 +0000 | |
4 | Subject: [PATCH 35/44] elf: Add TLS modid reuse test for bug 29039 | |
5 | ||
6 | This is a minimal regression test for bug 29039 which only affects | |
7 | targets with TLSDESC and a reproducer requires that | |
8 | ||
9 | 1) Have modid gaps (closed modules) with old generation. | |
10 | 2) Update a DTV to a newer generation (needs a newer dlopen). | |
11 | 3) But do not update the closed gap entry in that DTV. | |
12 | 4) Reuse the modid gap for a new module (another dlopen). | |
13 | 5) Use dynamic TLSDESC in that new module with old generation (bug). | |
14 | 6) Access TLS via this TLSDESC and the now outdated DTV. | |
15 | ||
16 | However step (3) in practice rarely happens: during DTV update the | |
17 | entries for closed modids are initialized to "unallocated" and then | |
18 | dynamic TLSDESC calls __tls_get_addr independently of its generation. | |
19 | The only exception to this is DTV setup at thread creation (gaps are | |
20 | initialized to NULL instead of unallocated) or DTV resize where the | |
21 | gap entries are outside the previous DTV array (again NULL instead | |
22 | of unallocated, and this requires loading > DTV_SURPLUS modules). | |
23 | ||
24 | So the bug can only cause NULL (+ offset) dereference, not use after | |
25 | free. And the easiest way to get (3) is via thread creation. | |
26 | ||
27 | Note that step (5) requires that the newly loaded module has larger | |
28 | TLS than the remaining optional static TLS. And for (6) there cannot | |
29 | be other TLS access or dlopen in the thread that updates the DTV. | |
30 | ||
31 | Tested on aarch64-linux-gnu. | |
32 | ||
33 | Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org> | |
34 | (cherry picked from commit 980450f12685326729d63ff72e93a996113bf073) | |
35 | --- | |
36 | elf/Makefile | 15 +++++++ | |
37 | elf/tst-tlsgap-mod0.c | 2 + | |
38 | elf/tst-tlsgap-mod1.c | 2 + | |
39 | elf/tst-tlsgap-mod2.c | 2 + | |
40 | elf/tst-tlsgap.c | 92 +++++++++++++++++++++++++++++++++++++++++++ | |
41 | 5 files changed, 113 insertions(+) | |
42 | create mode 100644 elf/tst-tlsgap-mod0.c | |
43 | create mode 100644 elf/tst-tlsgap-mod1.c | |
44 | create mode 100644 elf/tst-tlsgap-mod2.c | |
45 | create mode 100644 elf/tst-tlsgap.c | |
46 | ||
47 | diff --git a/elf/Makefile b/elf/Makefile | |
48 | index c00e2ccfc5..1a05a6aaca 100644 | |
49 | --- a/elf/Makefile | |
50 | +++ b/elf/Makefile | |
51 | @@ -459,6 +459,7 @@ tests += \ | |
52 | tst-tls21 \ | |
53 | tst-tlsalign \ | |
54 | tst-tlsalign-extern \ | |
55 | + tst-tlsgap \ | |
56 | tst-unique1 \ | |
57 | tst-unique2 \ | |
58 | tst-unwind-ctor \ | |
59 | @@ -883,6 +884,9 @@ modules-names += \ | |
60 | tst-tls20mod-bad \ | |
61 | tst-tls21mod \ | |
62 | tst-tlsalign-lib \ | |
63 | + tst-tlsgap-mod0 \ | |
64 | + tst-tlsgap-mod1 \ | |
65 | + tst-tlsgap-mod2 \ | |
66 | tst-tlsmod1 \ | |
67 | tst-tlsmod10 \ | |
68 | tst-tlsmod11 \ | |
69 | @@ -3009,3 +3013,14 @@ LDFLAGS-tst-dlclose-lazy-mod1.so = -Wl,-z,lazy,--no-as-needed | |
70 | $(objpfx)tst-dlclose-lazy-mod1.so: $(objpfx)tst-dlclose-lazy-mod2.so | |
71 | $(objpfx)tst-dlclose-lazy.out: \ | |
72 | $(objpfx)tst-dlclose-lazy-mod1.so $(objpfx)tst-dlclose-lazy-mod2.so | |
73 | + | |
74 | +$(objpfx)tst-tlsgap: $(shared-thread-library) | |
75 | +$(objpfx)tst-tlsgap.out: \ | |
76 | + $(objpfx)tst-tlsgap-mod0.so \ | |
77 | + $(objpfx)tst-tlsgap-mod1.so \ | |
78 | + $(objpfx)tst-tlsgap-mod2.so | |
79 | +ifeq (yes,$(have-mtls-dialect-gnu2)) | |
80 | +CFLAGS-tst-tlsgap-mod0.c += -mtls-dialect=gnu2 | |
81 | +CFLAGS-tst-tlsgap-mod1.c += -mtls-dialect=gnu2 | |
82 | +CFLAGS-tst-tlsgap-mod2.c += -mtls-dialect=gnu2 | |
83 | +endif | |
84 | diff --git a/elf/tst-tlsgap-mod0.c b/elf/tst-tlsgap-mod0.c | |
85 | new file mode 100644 | |
86 | index 0000000000..1478b0beac | |
87 | --- /dev/null | |
88 | +++ b/elf/tst-tlsgap-mod0.c | |
89 | @@ -0,0 +1,2 @@ | |
90 | +int __thread tls0; | |
91 | +int *f0(void) { return &tls0; } | |
92 | diff --git a/elf/tst-tlsgap-mod1.c b/elf/tst-tlsgap-mod1.c | |
93 | new file mode 100644 | |
94 | index 0000000000..b10fc3702c | |
95 | --- /dev/null | |
96 | +++ b/elf/tst-tlsgap-mod1.c | |
97 | @@ -0,0 +1,2 @@ | |
98 | +int __thread tls1[100]; /* Size > glibc.rtld.optional_static_tls / 2. */ | |
99 | +int *f1(void) { return tls1; } | |
100 | diff --git a/elf/tst-tlsgap-mod2.c b/elf/tst-tlsgap-mod2.c | |
101 | new file mode 100644 | |
102 | index 0000000000..166c27d7f3 | |
103 | --- /dev/null | |
104 | +++ b/elf/tst-tlsgap-mod2.c | |
105 | @@ -0,0 +1,2 @@ | |
106 | +int __thread tls2; | |
107 | +int *f2(void) { return &tls2; } | |
108 | diff --git a/elf/tst-tlsgap.c b/elf/tst-tlsgap.c | |
109 | new file mode 100644 | |
110 | index 0000000000..4932885076 | |
111 | --- /dev/null | |
112 | +++ b/elf/tst-tlsgap.c | |
113 | @@ -0,0 +1,92 @@ | |
114 | +/* TLS modid gap reuse regression test for bug 29039. | |
115 | + Copyright (C) 2023 Free Software Foundation, Inc. | |
116 | + This file is part of the GNU C Library. | |
117 | + | |
118 | + The GNU C Library is free software; you can redistribute it and/or | |
119 | + modify it under the terms of the GNU Lesser General Public | |
120 | + License as published by the Free Software Foundation; either | |
121 | + version 2.1 of the License, or (at your option) any later version. | |
122 | + | |
123 | + The GNU C Library is distributed in the hope that it will be useful, | |
124 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
125 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
126 | + Lesser General Public License for more details. | |
127 | + | |
128 | + You should have received a copy of the GNU Lesser General Public | |
129 | + License along with the GNU C Library; if not, see | |
130 | + <http://www.gnu.org/licenses/>. */ | |
131 | + | |
132 | +#include <stdio.h> | |
133 | +#include <dlfcn.h> | |
134 | +#include <pthread.h> | |
135 | +#include <support/xdlfcn.h> | |
136 | +#include <support/xthread.h> | |
137 | +#include <support/check.h> | |
138 | + | |
139 | +static void *mod[3]; | |
140 | +#define MOD(i) "tst-tlsgap-mod" #i ".so" | |
141 | +static const char *modname[3] = { MOD(0), MOD(1), MOD(2) }; | |
142 | +#undef MOD | |
143 | + | |
144 | +static void | |
145 | +open_mod (int i) | |
146 | +{ | |
147 | + mod[i] = xdlopen (modname[i], RTLD_LAZY); | |
148 | + printf ("open %s\n", modname[i]); | |
149 | +} | |
150 | + | |
151 | +static void | |
152 | +close_mod (int i) | |
153 | +{ | |
154 | + xdlclose (mod[i]); | |
155 | + mod[i] = NULL; | |
156 | + printf ("close %s\n", modname[i]); | |
157 | +} | |
158 | + | |
159 | +static void | |
160 | +access_mod (int i, const char *sym) | |
161 | +{ | |
162 | + int *(*f) (void) = xdlsym (mod[i], sym); | |
163 | + int *p = f (); | |
164 | + printf ("access %s: %s() = %p\n", modname[i], sym, p); | |
165 | + TEST_VERIFY_EXIT (p != NULL); | |
166 | + ++*p; | |
167 | +} | |
168 | + | |
169 | +static void * | |
170 | +start (void *arg) | |
171 | +{ | |
172 | + /* The DTV generation is at the last dlopen of mod0 and the | |
173 | + entry for mod1 is NULL. */ | |
174 | + | |
175 | + open_mod (1); /* Reuse modid of mod1. Uses dynamic TLS. */ | |
176 | + | |
177 | + /* DTV is unchanged: dlopen only updates the DTV to the latest | |
178 | + generation if static TLS is allocated for a loaded module. | |
179 | + | |
180 | + With bug 29039, the TLSDESC relocation in mod1 uses the old | |
181 | + dlclose generation of mod1 instead of the new dlopen one so | |
182 | + DTV is not updated on TLS access. */ | |
183 | + | |
184 | + access_mod (1, "f1"); | |
185 | + | |
186 | + return arg; | |
187 | +} | |
188 | + | |
189 | +static int | |
190 | +do_test (void) | |
191 | +{ | |
192 | + open_mod (0); | |
193 | + open_mod (1); | |
194 | + open_mod (2); | |
195 | + close_mod (0); | |
196 | + close_mod (1); /* Create modid gap at mod1. */ | |
197 | + open_mod (0); /* Reuse modid of mod0, bump generation count. */ | |
198 | + | |
199 | + /* Create a thread where DTV of mod1 is NULL. */ | |
200 | + pthread_t t = xpthread_create (NULL, start, NULL); | |
201 | + xpthread_join (t); | |
202 | + return 0; | |
203 | +} | |
204 | + | |
205 | +#include <support/test-driver.c> | |
206 | -- | |
207 | 2.39.2 | |
208 |