]>
Commit | Line | Data |
---|---|---|
fe875de8 MT |
1 | # |
2 | # This is a special patch for rhel-6 to fix recursive dlopen. | |
3 | # It is likely the upstream patch will always be too risky for | |
4 | # rhel-6 and will involve reorganizing the way in which recursive | |
5 | # dlopen is allowed to operate and how the _r_debug and stap | |
6 | # points are used by gdb for the recursive case. | |
7 | # | |
8 | # This fix changes the internal API to duplicate the ldconfig | |
9 | # cache data. This means that at any point the cache can be | |
10 | # unmapped without any consequences. The caller is responsible | |
11 | # fore freeing the returned string. | |
12 | # | |
13 | # A regression test is added to verify the assertion for _r_debug | |
14 | # is no longer triggered due to the recursive dlopen. The test to | |
15 | # verify the fix in _dl_load_cache_lookup is not automated and | |
16 | # has to be run by hand. | |
17 | # | |
18 | diff -urN glibc-2.12-2-gc4ccff1/elf/dl-cache.c glibc-2.12-2-gc4ccff1.mod/elf/dl-cache.c | |
19 | --- glibc-2.12-2-gc4ccff1/elf/dl-cache.c 2010-05-04 07:27:23.000000000 -0400 | |
20 | +++ glibc-2.12-2-gc4ccff1.mod/elf/dl-cache.c 2014-12-10 21:54:08.801985045 -0500 | |
21 | @@ -175,9 +175,12 @@ | |
22 | ||
23 | ||
24 | /* Look up NAME in ld.so.cache and return the file name stored there, | |
25 | - or null if none is found. */ | |
26 | - | |
27 | -const char * | |
28 | + or null if none is found. | |
29 | + The caller is responsible for freeing the returned string. The ld.so.cache | |
30 | + may be unmapped at any time by a completing recursive dlopen and | |
31 | + this function must take care that it does not return references to | |
32 | + any data in the mapping. */ | |
33 | +char * | |
34 | internal_function | |
35 | _dl_load_cache_lookup (const char *name) | |
36 | { | |
37 | @@ -290,7 +293,17 @@ | |
38 | && best != NULL) | |
39 | _dl_debug_printf (" trying file=%s\n", best); | |
40 | ||
41 | - return best; | |
42 | + if (best == NULL) | |
43 | + return NULL; | |
44 | + | |
45 | + /* The double copy is *required* since malloc may be interposed | |
46 | + and call dlopen itself whose completion would unmap the data | |
47 | + we are accessing. Therefore we must make the copy of the | |
48 | + mapping data without using malloc. */ | |
49 | + char *temp; | |
50 | + temp = alloca (strlen (best) + 1); | |
51 | + strcpy (temp, best); | |
52 | + return strdup (temp); | |
53 | } | |
54 | ||
55 | #ifndef MAP_COPY | |
56 | diff -urN glibc-2.12-2-gc4ccff1/elf/dl-load.c glibc-2.12-2-gc4ccff1.mod/elf/dl-load.c | |
57 | --- glibc-2.12-2-gc4ccff1/elf/dl-load.c 2014-12-10 11:03:17.966048404 -0500 | |
58 | +++ glibc-2.12-2-gc4ccff1.mod/elf/dl-load.c 2014-12-10 21:47:29.319387538 -0500 | |
59 | @@ -2126,7 +2126,7 @@ | |
60 | { | |
61 | /* Check the list of libraries in the file /etc/ld.so.cache, | |
62 | for compatibility with Linux's ldconfig program. */ | |
63 | - const char *cached = _dl_load_cache_lookup (name); | |
64 | + char *cached = _dl_load_cache_lookup (name); | |
65 | ||
66 | if (cached != NULL) | |
67 | { | |
68 | @@ -2156,6 +2156,7 @@ | |
69 | if (memcmp (cached, dirp, system_dirs_len[cnt]) == 0) | |
70 | { | |
71 | /* The prefix matches. Don't use the entry. */ | |
72 | + free (cached); | |
73 | cached = NULL; | |
74 | break; | |
75 | } | |
76 | @@ -2172,14 +2173,9 @@ | |
77 | &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded, | |
78 | LA_SER_CONFIG, &found_other_class, false); | |
79 | if (__builtin_expect (fd != -1, 1)) | |
80 | - { | |
81 | - realname = local_strdup (cached); | |
82 | - if (realname == NULL) | |
83 | - { | |
84 | - __close (fd); | |
85 | - fd = -1; | |
86 | - } | |
87 | - } | |
88 | + realname = cached; | |
89 | + else | |
90 | + free (cached); | |
91 | } | |
92 | } | |
93 | } | |
94 | diff -urN glibc-2.12-2-gc4ccff1/elf/dl-open.c glibc-2.12-2-gc4ccff1.mod/elf/dl-open.c | |
95 | --- glibc-2.12-2-gc4ccff1/elf/dl-open.c 2014-12-10 11:03:18.083048497 -0500 | |
96 | +++ glibc-2.12-2-gc4ccff1.mod/elf/dl-open.c 2014-12-10 20:34:16.017503638 -0500 | |
97 | @@ -220,7 +220,11 @@ | |
98 | } | |
99 | } | |
100 | ||
101 | - assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT); | |
102 | + /* One might be tempted to assert that we are RT_CONSISTENT at this point, but that | |
103 | + may not be true if this is a recursive call to dlopen. | |
104 | + TODO: Fix all of the debug state so we end up at RT_CONSISTENT only when the last | |
105 | + recursive dlopen completes. */ | |
106 | + _dl_debug_initialize (0, args->nsid); | |
107 | ||
108 | /* Load the named object. */ | |
109 | struct link_map *new; | |
110 | diff -urN glibc-2.12-2-gc4ccff1/sysdeps/generic/ldsodefs.h glibc-2.12-2-gc4ccff1.mod/sysdeps/generic/ldsodefs.h | |
111 | --- glibc-2.12-2-gc4ccff1/sysdeps/generic/ldsodefs.h 2014-12-10 11:03:17.944048387 -0500 | |
112 | +++ glibc-2.12-2-gc4ccff1.mod/sysdeps/generic/ldsodefs.h 2014-12-10 21:46:14.071344018 -0500 | |
113 | @@ -996,8 +996,8 @@ | |
114 | internal_function; | |
115 | ||
116 | /* Look up NAME in ld.so.cache and return the file name stored there, | |
117 | - or null if none is found. */ | |
118 | -extern const char *_dl_load_cache_lookup (const char *name) | |
119 | + or null if none is found. Caller must free returned string. */ | |
120 | +extern char *_dl_load_cache_lookup (const char *name) | |
121 | internal_function; | |
122 | ||
123 | /* If the system does not support MAP_COPY we cannot leave the file open | |
124 | diff -urN glibc-2.12-2-gc4ccff1/dlfcn/Makefile glibc-2.12-2-gc4ccff1.mod/dlfcn/Makefile | |
125 | --- glibc-2.12-2-gc4ccff1/dlfcn/Makefile 2010-05-04 07:27:23.000000000 -0400 | |
126 | +++ glibc-2.12-2-gc4ccff1.mod/dlfcn/Makefile 2014-12-11 16:58:55.719803063 -0500 | |
127 | @@ -42,12 +42,12 @@ | |
128 | ifeq (yes,$(build-shared)) | |
129 | tests = glrefmain failtest tst-dladdr default errmsg1 tstcxaatexit \ | |
130 | bug-dlopen1 bug-dlsym1 tst-dlinfo bug-atexit1 bug-atexit2 \ | |
131 | - bug-atexit3 tstatexit | |
132 | + bug-atexit3 tstatexit tst-rec-dlopen | |
133 | endif | |
134 | modules-names = glreflib1 glreflib2 glreflib3 failtestmod defaultmod1 \ | |
135 | defaultmod2 errmsg1mod modatexit modcxaatexit \ | |
136 | bug-dlsym1-lib1 bug-dlsym1-lib2 bug-atexit1-lib \ | |
137 | - bug-atexit2-lib bug-atexit3-lib | |
138 | + bug-atexit2-lib bug-atexit3-lib moddummy1 moddummy2 | |
139 | ||
140 | failtestmod.so-no-z-defs = yes | |
141 | glreflib2.so-no-z-defs = yes | |
142 | @@ -142,6 +142,8 @@ | |
143 | $(objpfx)bug-atexit3-lib.so: $(common-objpfx)libc.so \ | |
144 | $(common-objpfx)libc_nonshared.a | |
145 | ||
146 | +LDLIBS-tst-rec-dlopen = -ldl | |
147 | +$(objpfx)tst-rec-dlopen: $(libdl) | |
148 | ||
149 | # Depend on libc.so so a DT_NEEDED is generated in the shared objects. | |
150 | # This ensures they will load libc.so for needed symbols if loaded by | |
151 | diff -urN glibc-2.12-2-gc4ccff1/dlfcn/moddummy1.c glibc-2.12-2-gc4ccff1.mod/dlfcn/moddummy1.c | |
152 | --- glibc-2.12-2-gc4ccff1/dlfcn/moddummy1.c 1969-12-31 19:00:00.000000000 -0500 | |
153 | +++ glibc-2.12-2-gc4ccff1.mod/dlfcn/moddummy1.c 2014-12-11 16:57:54.108797285 -0500 | |
154 | @@ -0,0 +1,13 @@ | |
155 | +/* Provide a dummy DSO for tst-recursive-dlopen to use. */ | |
156 | +#include <stdio.h> | |
157 | +#include <stdlib.h> | |
158 | + | |
159 | +int called_dummy1; | |
160 | + | |
161 | +void | |
162 | +dummy1 (void) | |
163 | +{ | |
164 | + printf ("Called dummy1()\n"); | |
165 | + called_dummy1++; | |
166 | +} | |
167 | + | |
168 | diff -urN glibc-2.12-2-gc4ccff1/dlfcn/moddummy2.c glibc-2.12-2-gc4ccff1.mod/dlfcn/moddummy2.c | |
169 | --- glibc-2.12-2-gc4ccff1/dlfcn/moddummy2.c 1969-12-31 19:00:00.000000000 -0500 | |
170 | +++ glibc-2.12-2-gc4ccff1.mod/dlfcn/moddummy2.c 2014-12-11 16:57:54.108797285 -0500 | |
171 | @@ -0,0 +1,13 @@ | |
172 | +/* Provide a dummy DSO for tst-recursive-dlopen to use. */ | |
173 | +#include <stdio.h> | |
174 | +#include <stdlib.h> | |
175 | + | |
176 | +int called_dummy2; | |
177 | + | |
178 | +void | |
179 | +dummy2 (void) | |
180 | +{ | |
181 | + printf ("Called dummy2()\n"); | |
182 | + called_dummy2++; | |
183 | +} | |
184 | + | |
185 | diff -urN glibc-2.12-2-gc4ccff1/dlfcn/tst-rec-dlopen.c glibc-2.12-2-gc4ccff1.mod/dlfcn/tst-rec-dlopen.c | |
186 | --- glibc-2.12-2-gc4ccff1/dlfcn/tst-rec-dlopen.c 1969-12-31 19:00:00.000000000 -0500 | |
187 | +++ glibc-2.12-2-gc4ccff1.mod/dlfcn/tst-rec-dlopen.c 2014-12-11 20:53:28.617848774 -0500 | |
188 | @@ -0,0 +1,145 @@ | |
189 | +/* Test recursive dlopen using malloc hooks. | |
190 | + Copyright (C) 1998-2014 Free Software Foundation, Inc. | |
191 | + This file is part of the GNU C Library. | |
192 | + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998. | |
193 | + | |
194 | + The GNU C Library is free software; you can redistribute it and/or | |
195 | + modify it under the terms of the GNU Lesser General Public | |
196 | + License as published by the Free Software Foundation; either | |
197 | + version 2.1 of the License, or (at your option) any later version. | |
198 | + | |
199 | + The GNU C Library is distributed in the hope that it will be useful, | |
200 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
201 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
202 | + Lesser General Public License for more details. | |
203 | + | |
204 | + You should have received a copy of the GNU Lesser General Public | |
205 | + License along with the GNU C Library; if not, see | |
206 | + <http://www.gnu.org/licenses/>. */ | |
207 | + | |
208 | +#include <stdio.h> | |
209 | +#include <stdlib.h> | |
210 | +#include <malloc.h> | |
211 | +#include <dlfcn.h> | |
212 | + | |
213 | +#define DSO "moddummy1.so" | |
214 | +#define FUNC "dummy1" | |
215 | + | |
216 | +#define DSO1 "moddummy2.so" | |
217 | +#define FUNC1 "dummy2" | |
218 | + | |
219 | +/* Prevent the compiler from moving the assignment to called_func | |
220 | + before (*func)() since the compiler doesn't know we might abort | |
221 | + or catch a SIGSEGV signal and it may move the store. */ | |
222 | +volatile int called_func; | |
223 | + | |
224 | +/* Prototype for my hook. */ | |
225 | +void *custom_malloc_hook (size_t, const void *); | |
226 | + | |
227 | +/* Pointer to old malloc hooks. */ | |
228 | +void *(*old_malloc_hook) (size_t, const void *); | |
229 | + | |
230 | +/* Call function func_name in DSO dso_name via dlopen. */ | |
231 | +void | |
232 | +call_func (const char *dso_name, const char *func_name) | |
233 | +{ | |
234 | + int ret; | |
235 | + void *dso; | |
236 | + void (*func) (void); | |
237 | + char *err; | |
238 | + | |
239 | + /* Open the DSO. */ | |
240 | + dso = dlopen (dso_name, RTLD_NOW|RTLD_GLOBAL); | |
241 | + if (dso == NULL) | |
242 | + { | |
243 | + err = dlerror (); | |
244 | + fprintf (stderr, "%s\n", err); | |
245 | + exit (1); | |
246 | + } | |
247 | + /* Clear any errors. */ | |
248 | + dlerror (); | |
249 | + | |
250 | + /* Lookup func. */ | |
251 | + *(void **) (&func) = dlsym (dso, func_name); | |
252 | + if (func == NULL) | |
253 | + { | |
254 | + err = dlerror (); | |
255 | + if (err != NULL) | |
256 | + { | |
257 | + fprintf (stderr, "%s\n", err); | |
258 | + exit (1); | |
259 | + } | |
260 | + } | |
261 | + /* Call func. */ | |
262 | + (*func) (); | |
263 | + called_func = 1; | |
264 | + | |
265 | + /* Close the library and look for errors too. */ | |
266 | + ret = dlclose (dso); | |
267 | + if (ret != 0) | |
268 | + { | |
269 | + err = dlerror (); | |
270 | + fprintf (stderr, "%s\n", err); | |
271 | + exit (1); | |
272 | + } | |
273 | + | |
274 | +} | |
275 | + | |
276 | +/* Empty hook that does nothing. */ | |
277 | +void * | |
278 | +custom_malloc_hook (size_t size, const void *caller) | |
279 | +{ | |
280 | + void *result; | |
281 | + /* Restore old hooks. */ | |
282 | + __malloc_hook = old_malloc_hook; | |
283 | + /* First call a function in another library via dlopen. */ | |
284 | + call_func (DSO1, FUNC1); | |
285 | + /* Called recursively. */ | |
286 | + result = malloc (size); | |
287 | + /* Restore new hooks. */ | |
288 | + __malloc_hook = custom_malloc_hook; | |
289 | + return result; | |
290 | +} | |
291 | + | |
292 | +static int | |
293 | +do_test (void) | |
294 | +{ | |
295 | + /* Save old hook. */ | |
296 | + old_malloc_hook = __malloc_hook; | |
297 | + /* Install new hook. */ | |
298 | + __malloc_hook = custom_malloc_hook; | |
299 | + | |
300 | + /* Bug 17702 fixes two things: | |
301 | + * A recursive dlopen unmapping the ld.so.cache. | |
302 | + * An assertion that _r_debug is RT_CONSISTENT at entry to dlopen. | |
303 | + We can only test the latter. Testing the former requires modifying | |
304 | + ld.so.conf to cache the dummy libraries, then running ldconfig, | |
305 | + then run the test. If you do all of that (and glibc's test | |
306 | + infrastructure doesn't support that yet) then the test will | |
307 | + SEGFAULT without the fix. If you don't do that, then the test | |
308 | + will abort because of the assert described in detail below. */ | |
309 | + call_func (DSO, FUNC); | |
310 | + | |
311 | + /* Restore old hook. */ | |
312 | + __malloc_hook = old_malloc_hook; | |
313 | + | |
314 | + /* The function dummy2() is called by the malloc hook. Check to | |
315 | + see that it was called. This ensures the second recursive | |
316 | + dlopen happened and we called the function in that library. | |
317 | + | |
318 | + Before the fix you either get a SIGSEGV when accessing mmap'd | |
319 | + ld.so.cache data or an assertion failure about _r_debug not | |
320 | + beint RT_CONSISTENT. We don't test for the SIGSEGV since it | |
321 | + would require finding moddummy1 or moddummy2 in the cache and | |
322 | + we don't have any infrastructure to test that, but the _r_debug | |
323 | + assertion triggers. */ | |
324 | + if (called_func > 0) | |
325 | + printf ("PASS: Function call_func() called more than once.\n"); | |
326 | + else | |
327 | + printf ("FAIL: Function call_func() not called.\n"); | |
328 | + | |
329 | + return 0; | |
330 | +} | |
331 | + | |
332 | +#define TEST_FUNCTION do_test () | |
333 | +#include "../test-skeleton.c" |