]>
Commit | Line | Data |
---|---|---|
fe875de8 MT |
1 | commit d26dfc60edc8c6dd160eefff16a734152a835ca0 |
2 | Author: Martin von Gagern <Martin.vGagern@gmx.net> | |
3 | Date: Sat May 14 21:25:43 2011 -0400 | |
4 | ||
5 | Fix handling of static TLS in dlopen'ed objects | |
6 | ||
7 | When dynamically loading a library along with several dependencies, calls to | |
8 | _dl_add_to_slotinfo and _dl_update_slotinfo can become intermixed. As a | |
9 | consequence, _dl_update_slotinfo will update the generation counter of the dtv | |
10 | although not all of the slots belonging to that generation have been added. | |
11 | Subsequent calls to _dl_add_to_slotinfo will add more slots to the same | |
12 | generation, for which no storage will be allocated, as the dtv generation | |
13 | checks will claim no work is necessary. This will lead to uninitialized dtv | |
14 | entries and will likely cause a SIGSEGV when thread local variables are | |
15 | accessed. | |
16 | ||
17 | diff --git a/elf/Makefile b/elf/Makefile | |
18 | index 8d9657d..6efb86c 100644 | |
19 | --- a/elf/Makefile | |
20 | +++ b/elf/Makefile | |
21 | @@ -76,6 +76,7 @@ distribute := rtld-Rules \ | |
22 | tst-tlsmod12.c tst-tls10.h tst-alignmod.c tst-alignmod2.c \ | |
23 | circlemod1.c circlemod1a.c circlemod2.c circlemod2a.c \ | |
24 | circlemod3.c circlemod3a.c nodlopenmod2.c \ | |
25 | + tst-tls19mod1.c tst-tls19mod2.c tst-tls19mod3.c \ | |
26 | tls-macros.h \ | |
27 | reldep8mod1.c reldep8mod2.c reldep8mod3.c \ | |
28 | nodel2mod1.c nodel2mod2.c nodel2mod3.c \ | |
29 | @@ -194,7 +195,7 @@ tests += loadtest restest1 preloadtest loadfail multiload origtest resolvfail \ | |
30 | restest2 next dblload dblunload reldep5 reldep6 reldep7 reldep8 \ | |
31 | circleload1 tst-tls3 tst-tls4 tst-tls5 tst-tls6 tst-tls7 tst-tls8 \ | |
32 | tst-tls10 tst-tls11 tst-tls12 tst-tls13 tst-tls14 tst-tls15 \ | |
33 | - tst-tls16 tst-tls17 tst-tls18 tst-tls-dlinfo \ | |
34 | + tst-tls16 tst-tls17 tst-tls18 tst-tls19 tst-tls-dlinfo \ | |
35 | tst-align tst-align2 $(tests-execstack-$(have-z-execstack)) \ | |
36 | tst-dlmodcount tst-dlopenrpath tst-deep1 \ | |
37 | tst-dlmopen1 tst-dlmopen2 tst-dlmopen3 \ | |
38 | @@ -240,6 +241,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ | |
39 | $(patsubst %,tst-tlsmod17a%,$(tlsmod17a-suffixes)) \ | |
40 | tst-tlsmod17b \ | |
41 | $(patsubst %,tst-tlsmod18a%,$(tlsmod18a-suffixes)) \ | |
42 | + tst-tls19mod1 tst-tls19mod2 tst-tls19mod3 \ | |
43 | circlemod1 circlemod1a circlemod2 circlemod2a \ | |
44 | circlemod3 circlemod3a \ | |
45 | reldep8mod1 reldep8mod2 reldep8mod3 \ | |
46 | @@ -525,6 +527,8 @@ $(objpfx)tst-tlsmod13a.so: $(objpfx)tst-tlsmod13.so | |
47 | # For tst-tls9-static, make sure the modules it dlopens have libc.so in DT_NEEDED | |
48 | $(objpfx)tst-tlsmod5.so: $(common-objpfx)libc.so | |
49 | $(objpfx)tst-tlsmod6.so: $(common-objpfx)libc.so | |
50 | +$(objpfx)tst-tls19mod1.so: $(objpfx)tst-tls19mod2.so $(objpfx)tst-tls19mod3.so | |
51 | +$(objpfx)tst-tls19mod3.so: $(objpfx)ld.so | |
52 | $(objpfx)reldep8mod3.so: $(objpfx)reldep8mod1.so $(objpfx)reldep8mod2.so | |
53 | $(objpfx)nodel2mod3.so: $(objpfx)nodel2mod1.so $(objpfx)nodel2mod2.so | |
54 | $(objpfx)reldep9mod2.so: $(objpfx)reldep9mod1.so | |
55 | @@ -822,6 +826,9 @@ $(patsubst %,$(objpfx)%.os,$(tlsmod18a-modules)): $(objpfx)tst-tlsmod18a%.os : t | |
56 | $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ -DN=$* -DNOT_IN_libc=1 $< | |
57 | $(patsubst %,$(objpfx)tst-tlsmod18a%.so,$(tlsmod18a-suffixes)): $(objpfx)tst-tlsmod18a%.so: $(objpfx)ld.so | |
58 | ||
59 | +$(objpfx)tst-tls19: $(libdl) | |
60 | +$(objpfx)tst-tls19.out: $(objpfx)tst-tls19mod1.so | |
61 | + | |
62 | CFLAGS-tst-align.c = $(stack-align-test-flags) | |
63 | CFLAGS-tst-align2.c = $(stack-align-test-flags) | |
64 | CFLAGS-tst-alignmod.c = $(stack-align-test-flags) | |
65 | diff --git a/elf/dl-open.c b/elf/dl-open.c | |
66 | index cf8e8cc..8d90b56 100644 | |
67 | --- a/elf/dl-open.c | |
68 | +++ b/elf/dl-open.c | |
69 | @@ -347,6 +347,7 @@ dl_open_worker (void *a) | |
70 | /* If the file is not loaded now as a dependency, add the search | |
71 | list of the newly loaded object to the scope. */ | |
72 | bool any_tls = false; | |
73 | + unsigned int first_static_tls = new->l_searchlist.r_nlist; | |
74 | for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) | |
75 | { | |
76 | struct link_map *imap = new->l_searchlist.r_list[i]; | |
77 | @@ -425,30 +426,9 @@ dl_open_worker (void *a) | |
78 | might have to increase its size. */ | |
79 | _dl_add_to_slotinfo (imap); | |
80 | ||
81 | - if (imap->l_need_tls_init) | |
82 | - { | |
83 | - /* For static TLS we have to allocate the memory here | |
84 | - and now. This includes allocating memory in the DTV. | |
85 | - But we cannot change any DTV other than our own. So, | |
86 | - if we cannot guarantee that there is room in the DTV | |
87 | - we don't even try it and fail the load. | |
88 | - | |
89 | - XXX We could track the minimum DTV slots allocated in | |
90 | - all threads. */ | |
91 | - if (! RTLD_SINGLE_THREAD_P && imap->l_tls_modid > DTV_SURPLUS) | |
92 | - _dl_signal_error (0, "dlopen", NULL, N_("\ | |
93 | -cannot load any more object with static TLS")); | |
94 | - | |
95 | - imap->l_need_tls_init = 0; | |
96 | -#ifdef SHARED | |
97 | - /* Update the slot information data for at least the | |
98 | - generation of the DSO we are allocating data for. */ | |
99 | - _dl_update_slotinfo (imap->l_tls_modid); | |
100 | -#endif | |
101 | - | |
102 | - GL(dl_init_static_tls) (imap); | |
103 | - assert (imap->l_need_tls_init == 0); | |
104 | - } | |
105 | + if (imap->l_need_tls_init | |
106 | + && first_static_tls == new->l_searchlist.r_nlist) | |
107 | + first_static_tls = i; | |
108 | ||
109 | /* We have to bump the generation counter. */ | |
110 | any_tls = true; | |
111 | @@ -460,6 +440,40 @@ cannot load any more object with static TLS")); | |
112 | _dl_fatal_printf (N_("\ | |
113 | TLS generation counter wrapped! Please report this.")); | |
114 | ||
115 | + /* We need a second pass for static tls data, because _dl_update_slotinfo | |
116 | + must not be run while calls to _dl_add_to_slotinfo are still pending. */ | |
117 | + for (unsigned int i = first_static_tls; i < new->l_searchlist.r_nlist; ++i) | |
118 | + { | |
119 | + struct link_map *imap = new->l_searchlist.r_list[i]; | |
120 | + | |
121 | + if (imap->l_need_tls_init | |
122 | + && ! imap->l_init_called | |
123 | + && imap->l_tls_blocksize > 0) | |
124 | + { | |
125 | + /* For static TLS we have to allocate the memory here and | |
126 | + now. This includes allocating memory in the DTV. But we | |
127 | + cannot change any DTV other than our own. So, if we | |
128 | + cannot guarantee that there is room in the DTV we don't | |
129 | + even try it and fail the load. | |
130 | + | |
131 | + XXX We could track the minimum DTV slots allocated in | |
132 | + all threads. */ | |
133 | + if (! RTLD_SINGLE_THREAD_P && imap->l_tls_modid > DTV_SURPLUS) | |
134 | + _dl_signal_error (0, "dlopen", NULL, N_("\ | |
135 | +cannot load any more object with static TLS")); | |
136 | + | |
137 | + imap->l_need_tls_init = 0; | |
138 | +#ifdef SHARED | |
139 | + /* Update the slot information data for at least the | |
140 | + generation of the DSO we are allocating data for. */ | |
141 | + _dl_update_slotinfo (imap->l_tls_modid); | |
142 | +#endif | |
143 | + | |
144 | + GL(dl_init_static_tls) (imap); | |
145 | + assert (imap->l_need_tls_init == 0); | |
146 | + } | |
147 | + } | |
148 | + | |
149 | /* Notify the debugger all new objects have been relocated. */ | |
150 | if (relocation_in_progress) | |
151 | LIBC_PROBE (rtld_reloc_complete, 3, args->nsid, r, new); | |
152 | diff --git a/elf/tst-tls19.c b/elf/tst-tls19.c | |
153 | new file mode 100644 | |
154 | index 0000000..acbc1d6 | |
155 | --- /dev/null | |
156 | +++ b/elf/tst-tls19.c | |
157 | @@ -0,0 +1,27 @@ | |
158 | +// BZ 12453 | |
159 | +#include <stdio.h> | |
160 | +#include <dlfcn.h> | |
161 | + | |
162 | + | |
163 | +static int | |
164 | +do_test (void) | |
165 | +{ | |
166 | + void* dl = dlopen ("tst-tls19mod1.so", RTLD_LAZY | RTLD_GLOBAL); | |
167 | + if (dl == NULL) | |
168 | + { | |
169 | + printf ("Error loading tst-tls19mod1.so: %s\n", dlerror ()); | |
170 | + return 1; | |
171 | + } | |
172 | + | |
173 | + int (*fn) (void) = dlsym (dl, "foo"); | |
174 | + if (fn == NULL) | |
175 | + { | |
176 | + printf("Error obtaining symbol foo\n"); | |
177 | + return 1; | |
178 | + } | |
179 | + | |
180 | + return fn (); | |
181 | +} | |
182 | + | |
183 | +#define TEST_FUNCTION do_test () | |
184 | +#include "../test-skeleton.c" | |
185 | diff --git a/elf/tst-tls19mod1.c b/elf/tst-tls19mod1.c | |
186 | new file mode 100644 | |
187 | index 0000000..2790097 | |
188 | --- /dev/null | |
189 | +++ b/elf/tst-tls19mod1.c | |
190 | @@ -0,0 +1,15 @@ | |
191 | +#include <stdio.h> | |
192 | + | |
193 | +extern int bar (void); | |
194 | +extern int baz (void); | |
195 | + | |
196 | +int | |
197 | +foo (void) | |
198 | +{ | |
199 | + int v1 = bar (); | |
200 | + int v2 = baz (); | |
201 | + | |
202 | + printf ("bar=%d, baz=%d\n", v1, v2); | |
203 | + | |
204 | + return v1 != 666 || v2 != 42; | |
205 | +} | |
206 | diff --git a/elf/tst-tls19mod2.c b/elf/tst-tls19mod2.c | |
207 | new file mode 100644 | |
208 | index 0000000..cae702f | |
209 | --- /dev/null | |
210 | +++ b/elf/tst-tls19mod2.c | |
211 | @@ -0,0 +1,13 @@ | |
212 | +static int __thread tbar __attribute__ ((tls_model ("initial-exec"))) = 666; | |
213 | + | |
214 | +void | |
215 | +setter (int a) | |
216 | +{ | |
217 | + tbar = a; | |
218 | +} | |
219 | + | |
220 | +int | |
221 | +bar (void) | |
222 | +{ | |
223 | + return tbar; | |
224 | +} | |
225 | diff --git a/elf/tst-tls19mod3.c b/elf/tst-tls19mod3.c | |
226 | new file mode 100644 | |
227 | index 0000000..e7b2801 | |
228 | --- /dev/null | |
229 | +++ b/elf/tst-tls19mod3.c | |
230 | @@ -0,0 +1,16 @@ | |
231 | +#include <stdio.h> | |
232 | + | |
233 | +static int __thread tbaz __attribute__ ((tls_model ("local-dynamic"))) = 42; | |
234 | + | |
235 | +void | |
236 | +setter2 (int a) | |
237 | +{ | |
238 | + tbaz = a; | |
239 | +} | |
240 | + | |
241 | +int | |
242 | +baz (void) | |
243 | +{ | |
244 | + printf ("&tbaz=%p\n", &tbaz); | |
245 | + return tbaz; | |
246 | +} |