mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-21 12:30:06 +00:00
Fix recursive dlopen.
The ability to recursively call dlopen is useful for malloc implementations that wish to load other dynamic modules that implement reentrant/AS-safe functions to use in their own implementation. Given that a user malloc implementation may be called by an ongoing dlopen to allocate memory the user malloc implementation interrupts dlopen and if it calls dlopen again that's a reentrant call. This patch fixes the issues with the ld.so.cache mapping and the _r_debug assertion which prevent this from working as expected. See: https://sourceware.org/ml/libc-alpha/2014-12/msg00446.html
This commit is contained in:
parent
042e1521c7
commit
ccdb048df4
19
ChangeLog
19
ChangeLog
@ -1,3 +1,22 @@
|
|||||||
|
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>
|
2015-01-21 Torvald Riegel <triegel@redhat.com>
|
||||||
Carlos O'Donell <carlos@redhat.com>
|
Carlos O'Donell <carlos@redhat.com>
|
||||||
|
|
||||||
|
7
NEWS
7
NEWS
@ -15,9 +15,10 @@ Version 2.21
|
|||||||
17485, 17501, 17506, 17508, 17522, 17555, 17570, 17571, 17572, 17573,
|
17485, 17501, 17506, 17508, 17522, 17555, 17570, 17571, 17572, 17573,
|
||||||
17574, 17582, 17583, 17584, 17585, 17589, 17594, 17601, 17608, 17616,
|
17574, 17582, 17583, 17584, 17585, 17589, 17594, 17601, 17608, 17616,
|
||||||
17625, 17630, 17633, 17634, 17635, 17647, 17653, 17657, 17658, 17664,
|
17625, 17630, 17633, 17634, 17635, 17647, 17653, 17657, 17658, 17664,
|
||||||
17665, 17668, 17682, 17717, 17719, 17722, 17723, 17724, 17725, 17732,
|
17665, 17668, 17682, 17702, 17717, 17719, 17722, 17723, 17724, 17725,
|
||||||
17733, 17744, 17745, 17746, 17747, 17748, 17775, 17777, 17780, 17781,
|
17732, 17733, 17744, 17745, 17746, 17747, 17748, 17775, 17777, 17780,
|
||||||
17782, 17791, 17793, 17796, 17797, 17803, 17806, 17834, 17844, 17848
|
17781, 17782, 17791, 17793, 17796, 17797, 17803, 17806, 17834, 17844,
|
||||||
|
17848
|
||||||
|
|
||||||
* A new semaphore algorithm has been implemented in generic C code for all
|
* A new semaphore algorithm has been implemented in generic C code for all
|
||||||
machines. Previous custom assembly implementations of semaphore were
|
machines. Previous custom assembly implementations of semaphore were
|
||||||
|
@ -36,13 +36,13 @@ endif
|
|||||||
ifeq (yes,$(build-shared))
|
ifeq (yes,$(build-shared))
|
||||||
tests = glrefmain failtest tst-dladdr default errmsg1 tstcxaatexit \
|
tests = glrefmain failtest tst-dladdr default errmsg1 tstcxaatexit \
|
||||||
bug-dlopen1 bug-dlsym1 tst-dlinfo bug-atexit1 bug-atexit2 \
|
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
|
endif
|
||||||
modules-names = glreflib1 glreflib2 glreflib3 failtestmod defaultmod1 \
|
modules-names = glreflib1 glreflib2 glreflib3 failtestmod defaultmod1 \
|
||||||
defaultmod2 errmsg1mod modatexit modcxaatexit \
|
defaultmod2 errmsg1mod modatexit modcxaatexit \
|
||||||
bug-dlsym1-lib1 bug-dlsym1-lib2 bug-atexit1-lib \
|
bug-dlsym1-lib1 bug-dlsym1-lib2 bug-atexit1-lib \
|
||||||
bug-atexit2-lib bug-atexit3-lib bug-dl-leaf-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
|
failtestmod.so-no-z-defs = yes
|
||||||
glreflib2.so-no-z-defs = yes
|
glreflib2.so-no-z-defs = yes
|
||||||
@ -139,3 +139,6 @@ $(objpfx)bug-dl-leaf: $(objpfx)bug-dl-leaf-lib.so
|
|||||||
$(objpfx)bug-dl-leaf.out: $(objpfx)bug-dl-leaf-lib-cb.so
|
$(objpfx)bug-dl-leaf.out: $(objpfx)bug-dl-leaf-lib-cb.so
|
||||||
$(objpfx)bug-dl-leaf-lib.so: $(libdl)
|
$(objpfx)bug-dl-leaf-lib.so: $(libdl)
|
||||||
$(objpfx)bug-dl-leaf-lib-cb.so: $(objpfx)bug-dl-leaf-lib.so
|
$(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
|
||||||
|
10
dlfcn/moddummy1.c
Normal file
10
dlfcn/moddummy1.c
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
/* 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;
|
||||||
|
}
|
13
dlfcn/moddummy2.c
Normal file
13
dlfcn/moddummy2.c
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/* 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;
|
||||||
|
}
|
144
dlfcn/tst-rec-dlopen.c
Normal file
144
dlfcn/tst-rec-dlopen.c
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
/* 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"
|
@ -174,9 +174,12 @@ _dl_cache_libcmp (const char *p1, const char *p2)
|
|||||||
|
|
||||||
/* Look up NAME in ld.so.cache and return the file name stored there, or null
|
/* 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
|
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. */
|
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
|
||||||
const char *
|
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
|
internal_function
|
||||||
_dl_load_cache_lookup (const char *name)
|
_dl_load_cache_lookup (const char *name)
|
||||||
{
|
{
|
||||||
@ -289,7 +292,17 @@ _dl_load_cache_lookup (const char *name)
|
|||||||
&& best != NULL)
|
&& best != NULL)
|
||||||
_dl_debug_printf (" trying file=%s\n", best);
|
_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
|
#ifndef MAP_COPY
|
||||||
|
@ -2051,7 +2051,7 @@ _dl_map_object (struct link_map *loader, const char *name,
|
|||||||
{
|
{
|
||||||
/* Check the list of libraries in the file /etc/ld.so.cache,
|
/* Check the list of libraries in the file /etc/ld.so.cache,
|
||||||
for compatibility with Linux's ldconfig program. */
|
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 (cached != NULL)
|
||||||
{
|
{
|
||||||
@ -2075,6 +2075,7 @@ _dl_map_object (struct link_map *loader, const char *name,
|
|||||||
if (memcmp (cached, dirp, system_dirs_len[cnt]) == 0)
|
if (memcmp (cached, dirp, system_dirs_len[cnt]) == 0)
|
||||||
{
|
{
|
||||||
/* The prefix matches. Don't use the entry. */
|
/* The prefix matches. Don't use the entry. */
|
||||||
|
free (cached);
|
||||||
cached = NULL;
|
cached = NULL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2092,14 +2093,9 @@ _dl_map_object (struct link_map *loader, const char *name,
|
|||||||
LA_SER_CONFIG, mode, &found_other_class,
|
LA_SER_CONFIG, mode, &found_other_class,
|
||||||
false);
|
false);
|
||||||
if (__glibc_likely (fd != -1))
|
if (__glibc_likely (fd != -1))
|
||||||
{
|
realname = cached;
|
||||||
realname = __strdup (cached);
|
else
|
||||||
if (realname == NULL)
|
free (cached);
|
||||||
{
|
|
||||||
__close (fd);
|
|
||||||
fd = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -217,7 +217,9 @@ dl_open_worker (void *a)
|
|||||||
args->nsid = call_map->l_ns;
|
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. */
|
/* Load the named object. */
|
||||||
struct link_map *new;
|
struct link_map *new;
|
||||||
|
@ -905,8 +905,8 @@ extern const struct r_strlenpair *_dl_important_hwcaps (const char *platform,
|
|||||||
internal_function;
|
internal_function;
|
||||||
|
|
||||||
/* Look up NAME in ld.so.cache and return the file name stored there,
|
/* Look up NAME in ld.so.cache and return the file name stored there,
|
||||||
or null if none is found. */
|
or null if none is found. Caller must free returned string. */
|
||||||
extern const char *_dl_load_cache_lookup (const char *name)
|
extern char *_dl_load_cache_lookup (const char *name)
|
||||||
internal_function;
|
internal_function;
|
||||||
|
|
||||||
/* If the system does not support MAP_COPY we cannot leave the file open
|
/* If the system does not support MAP_COPY we cannot leave the file open
|
||||||
|
Loading…
Reference in New Issue
Block a user