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.
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.
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.
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
24 /* Look up NAME in ld.so.cache and return the file name stored there,
25 - or null if none is found. */
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. */
35 _dl_load_cache_lookup (const char *name)
39 _dl_debug_printf (" trying file=%s\n", best);
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. */
50 + temp = alloca (strlen (best) + 1);
51 + strcpy (temp, best);
52 + return strdup (temp);
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
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);
69 if (memcmp (cached, dirp, system_dirs_len[cnt]) == 0)
71 /* The prefix matches. Don't use the entry. */
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))
81 - realname = local_strdup (cached);
82 - if (realname == NULL)
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
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);
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
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)
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
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
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
140 failtestmod.so-no-z-defs = yes
141 glreflib2.so-no-z-defs = yes
143 $(objpfx)bug-atexit3-lib.so: $(common-objpfx)libc.so \
144 $(common-objpfx)libc_nonshared.a
146 +LDLIBS-tst-rec-dlopen = -ldl
147 +$(objpfx)tst-rec-dlopen: $(libdl)
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
155 +/* Provide a dummy DSO for tst-recursive-dlopen to use. */
164 + printf ("Called dummy1()\n");
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
172 +/* Provide a dummy DSO for tst-recursive-dlopen to use. */
181 + printf ("Called dummy2()\n");
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
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.
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.
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.
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/>. */
213 +#define DSO "moddummy1.so"
214 +#define FUNC "dummy1"
216 +#define DSO1 "moddummy2.so"
217 +#define FUNC1 "dummy2"
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;
224 +/* Prototype for my hook. */
225 +void *custom_malloc_hook (size_t, const void *);
227 +/* Pointer to old malloc hooks. */
228 +void *(*old_malloc_hook) (size_t, const void *);
230 +/* Call function func_name in DSO dso_name via dlopen. */
232 +call_func (const char *dso_name, const char *func_name)
236 + void (*func) (void);
239 + /* Open the DSO. */
240 + dso = dlopen (dso_name, RTLD_NOW|RTLD_GLOBAL);
244 + fprintf (stderr, "%s\n", err);
247 + /* Clear any errors. */
251 + *(void **) (&func) = dlsym (dso, func_name);
257 + fprintf (stderr, "%s\n", err);
265 + /* Close the library and look for errors too. */
266 + ret = dlclose (dso);
270 + fprintf (stderr, "%s\n", err);
276 +/* Empty hook that does nothing. */
278 +custom_malloc_hook (size_t size, const void *caller)
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;
295 + /* Save old hook. */
296 + old_malloc_hook = __malloc_hook;
297 + /* Install new hook. */
298 + __malloc_hook = custom_malloc_hook;
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);
311 + /* Restore old hook. */
312 + __malloc_hook = old_malloc_hook;
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.
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");
327 + printf ("FAIL: Function call_func() not called.\n");
332 +#define TEST_FUNCTION do_test ()
333 +#include "../test-skeleton.c"