+2015-01-21 Carlos O'Donell <carlos@redhat.com>
+
+ [BZ #17702]
+ * dlfcn/Makefile (tests): Add tst-rec-dlopen.
+ (modules-names): Add moddummy1 and moddummy2.
+ ($(objpfx)tst-rec-dlopen): Define.
+ * dlfcn/moddummy1.c: New file.
+ * dlfcn/moddummy2.c: New file.
+ * dlfcn/tst-rec-dlopen.c: New file.
+ * elf/dl-cache.c (_dl_load_cache_lookup):
+ Return char*. Copy result with alloca/strcpy/strdup.
+ * elf/dl-load.c (_dl_map_object): _dl_load_cached_lookup
+ returns char*. Free cached. If not saving realname
+ free cached.
+ * elf/dl-open.c (dl_open_worker): Do not assert that
+ _r_debug->r_state is RT_CONSISTENT.
+ * sysdeps/generic/ldsodefs.h: _dl_load_cache_lookup
+ returns char*.
+
2015-01-21 Torvald Riegel <triegel@redhat.com>
Carlos O'Donell <carlos@redhat.com>
17485, 17501, 17506, 17508, 17522, 17555, 17570, 17571, 17572, 17573,
17574, 17582, 17583, 17584, 17585, 17589, 17594, 17601, 17608, 17616,
17625, 17630, 17633, 17634, 17635, 17647, 17653, 17657, 17658, 17664,
- 17665, 17668, 17682, 17717, 17719, 17722, 17723, 17724, 17725, 17732,
- 17733, 17744, 17745, 17746, 17747, 17748, 17775, 17777, 17780, 17781,
- 17782, 17791, 17793, 17796, 17797, 17803, 17806, 17834, 17844, 17848
+ 17665, 17668, 17682, 17702, 17717, 17719, 17722, 17723, 17724, 17725,
+ 17732, 17733, 17744, 17745, 17746, 17747, 17748, 17775, 17777, 17780,
+ 17781, 17782, 17791, 17793, 17796, 17797, 17803, 17806, 17834, 17844,
+ 17848
* A new semaphore algorithm has been implemented in generic C code for all
machines. Previous custom assembly implementations of semaphore were
ifeq (yes,$(build-shared))
tests = glrefmain failtest tst-dladdr default errmsg1 tstcxaatexit \
bug-dlopen1 bug-dlsym1 tst-dlinfo bug-atexit1 bug-atexit2 \
- bug-atexit3 tstatexit bug-dl-leaf
+ bug-atexit3 tstatexit bug-dl-leaf tst-rec-dlopen
endif
modules-names = glreflib1 glreflib2 glreflib3 failtestmod defaultmod1 \
defaultmod2 errmsg1mod modatexit modcxaatexit \
bug-dlsym1-lib1 bug-dlsym1-lib2 bug-atexit1-lib \
bug-atexit2-lib bug-atexit3-lib bug-dl-leaf-lib \
- bug-dl-leaf-lib-cb
+ bug-dl-leaf-lib-cb moddummy1 moddummy2
failtestmod.so-no-z-defs = yes
glreflib2.so-no-z-defs = yes
$(objpfx)bug-dl-leaf.out: $(objpfx)bug-dl-leaf-lib-cb.so
$(objpfx)bug-dl-leaf-lib.so: $(libdl)
$(objpfx)bug-dl-leaf-lib-cb.so: $(objpfx)bug-dl-leaf-lib.so
+
+$(objpfx)tst-rec-dlopen: $(libdl)
+$(objpfx)tst-rec-dlopen.out: $(objpfx)moddummy1.so $(objpfx)moddummy2.so
--- /dev/null
+/* Provide a dummy DSO for tst-rec-dlopen to use. */
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+dummy1 (void)
+{
+ printf ("Called dummy1()\n");
+ return 1;
+}
--- /dev/null
+/* Provide a dummy DSO for tst-rec-dlopen to use. */
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+dummy2 (void)
+{
+ printf ("Called dummy2()\n");
+ /* If the outer dlopen is not dummy1 (becuase of some error)
+ then tst-rec-dlopen will see a value of -1 as the returned
+ result and fail. */
+ return -1;
+}
--- /dev/null
+/* Test recursive dlopen using malloc hooks.
+ Copyright (C) 1998-2014 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+ Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
+
+ 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 <stdlib.h>
+#include <malloc.h>
+#include <dlfcn.h>
+
+#define DSO "moddummy1.so"
+#define FUNC "dummy1"
+
+#define DSO1 "moddummy2.so"
+#define FUNC1 "dummy2"
+
+/* Result of the called function. */
+int func_result;
+
+/* Prototype for my hook. */
+void *custom_malloc_hook (size_t, const void *);
+
+/* Pointer to old malloc hooks. */
+void *(*old_malloc_hook) (size_t, const void *);
+
+/* Call function func_name in DSO dso_name via dlopen. */
+void
+call_func (const char *dso_name, const char *func_name)
+{
+ int ret;
+ void *dso;
+ int (*func) (void);
+ char *err;
+
+ /* Open the DSO. */
+ dso = dlopen (dso_name, RTLD_NOW|RTLD_GLOBAL);
+ if (dso == NULL)
+ {
+ err = dlerror ();
+ fprintf (stderr, "%s\n", err);
+ exit (1);
+ }
+ /* Clear any errors. */
+ dlerror ();
+
+ /* Lookup func. */
+ *(void **) (&func) = dlsym (dso, func_name);
+ if (func == NULL)
+ {
+ err = dlerror ();
+ if (err != NULL)
+ {
+ fprintf (stderr, "%s\n", err);
+ exit (1);
+ }
+ }
+ /* Call func. */
+ func_result = (*func) ();
+
+ /* Close the library and look for errors too. */
+ ret = dlclose (dso);
+ if (ret != 0)
+ {
+ err = dlerror ();
+ fprintf (stderr, "%s\n", err);
+ exit (1);
+ }
+
+}
+
+/* Empty hook that does nothing. */
+void *
+custom_malloc_hook (size_t size, const void *caller)
+{
+ void *result;
+ /* Restore old hooks. */
+ __malloc_hook = old_malloc_hook;
+ /* First call a function in another library via dlopen. */
+ call_func (DSO1, FUNC1);
+ /* Called recursively. */
+ result = malloc (size);
+ /* Restore new hooks. */
+ __malloc_hook = custom_malloc_hook;
+ return result;
+}
+
+static int
+do_test (void)
+{
+ /* Save old hook. */
+ old_malloc_hook = __malloc_hook;
+ /* Install new hook. */
+ __malloc_hook = custom_malloc_hook;
+
+ /* Bug 17702 fixes two things:
+ * A recursive dlopen unmapping the ld.so.cache.
+ * An assertion that _r_debug is RT_CONSISTENT at entry to dlopen.
+ We can only test the latter. Testing the former requires modifying
+ ld.so.conf to cache the dummy libraries, then running ldconfig,
+ then run the test. If you do all of that (and glibc's test
+ infrastructure doesn't support that yet) then the test will
+ SEGFAULT without the fix. If you don't do that, then the test
+ will abort because of the assert described in detail below. */
+ call_func (DSO, FUNC);
+
+ /* Restore old hook. */
+ __malloc_hook = old_malloc_hook;
+
+ /* The function dummy2() is called by the malloc hook. Check to
+ see that it was called. This ensures the second recursive
+ dlopen happened and we called the function in that library.
+ Before the fix you either get a SIGSEGV when accessing mmap'd
+ ld.so.cache data or an assertion failure about _r_debug not
+ beint RT_CONSISTENT. We don't test for the SIGSEGV since it
+ would require finding moddummy1 or moddummy2 in the cache and
+ we don't have any infrastructure to test that, but the _r_debug
+ assertion triggers. */
+ printf ("Returned result is %d\n", func_result);
+ if (func_result <= 0)
+ {
+ printf ("FAIL: Function call_func() not called.\n");
+ exit (1);
+ }
+
+ printf ("PASS: Function call_func() called more than once.\n");
+ return 0;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
/* Look up NAME in ld.so.cache and return the file name stored there, or null
if none is found. The cache is loaded if it was not already. If loading
- the cache previously failed there will be no more attempts to load it. */
-
-const char *
+ the cache previously failed there will be no more attempts to load it.
+ The caller is responsible for freeing the returned string. The ld.so.cache
+ may be unmapped at any time by a completing recursive dlopen and
+ this function must take care that it does not return references to
+ any data in the mapping. */
+char *
internal_function
_dl_load_cache_lookup (const char *name)
{
&& best != NULL)
_dl_debug_printf (" trying file=%s\n", best);
- return best;
+ if (best == NULL)
+ return NULL;
+
+ /* The double copy is *required* since malloc may be interposed
+ and call dlopen itself whose completion would unmap the data
+ we are accessing. Therefore we must make the copy of the
+ mapping data without using malloc. */
+ char *temp;
+ temp = alloca (strlen (best) + 1);
+ strcpy (temp, best);
+ return strdup (temp);
}
#ifndef MAP_COPY
{
/* Check the list of libraries in the file /etc/ld.so.cache,
for compatibility with Linux's ldconfig program. */
- const char *cached = _dl_load_cache_lookup (name);
+ char *cached = _dl_load_cache_lookup (name);
if (cached != NULL)
{
if (memcmp (cached, dirp, system_dirs_len[cnt]) == 0)
{
/* The prefix matches. Don't use the entry. */
+ free (cached);
cached = NULL;
break;
}
LA_SER_CONFIG, mode, &found_other_class,
false);
if (__glibc_likely (fd != -1))
- {
- realname = __strdup (cached);
- if (realname == NULL)
- {
- __close (fd);
- fd = -1;
- }
- }
+ realname = cached;
+ else
+ free (cached);
}
}
}
args->nsid = call_map->l_ns;
}
- assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
+ /* One might be tempted to assert that we are RT_CONSISTENT at this point, but that
+ may not be true if this is a recursive call to dlopen. */
+ _dl_debug_initialize (0, args->nsid);
/* Load the named object. */
struct link_map *new;
internal_function;
/* Look up NAME in ld.so.cache and return the file name stored there,
- or null if none is found. */
-extern const char *_dl_load_cache_lookup (const char *name)
+ or null if none is found. Caller must free returned string. */
+extern char *_dl_load_cache_lookup (const char *name)
internal_function;
/* If the system does not support MAP_COPY we cannot leave the file open