1
0
mirror of https://sourceware.org/git/glibc.git synced 2025-07-29 11:41:21 +03:00

* sysdeps/generic/ldsodefs.h (DL_LOOKUP_GSCOPE_LOCK): New definition.

* elf/dl-runtime.c (_dl_fixup, _dl_profile_fixup): Or in
	DL_LOOKUP_GSCOPE_LOCK into flags after THREAD_GSCOPE_SET_FLAG ().
	* elf/dl-sym.c (do_sym): Likewise.
	* include/link.h (struct link_map): Add l_serial field.
	* elf/dl-object.c (_dl_new_object): Initialize l_serial.
	* elf/dl-lookup.c (add_dependency): Add flags argument.
	Remember map->l_serial, if DL_LOOKUP_GSCOPE_LOCK is among
	flags, use THREAD_GSCOPE_RESET_FLAG before and
	THREAD_GSCOPE_SET_FLAG after
	__rtld_lock_lock_recursive (GL(dl_load_lock)) to avoid deadlock.
	Don't dereference map until it has been found on some list.
	If map->l_serial changed, return -1.
This commit is contained in:
Ulrich Drepper
2007-09-18 19:20:56 +00:00
parent 874aa52349
commit b90395e6e9
7 changed files with 92 additions and 27 deletions

View File

@ -1,3 +1,19 @@
2007-09-18 Jakub Jelinek <jakub@redhat.com>
* sysdeps/generic/ldsodefs.h (DL_LOOKUP_GSCOPE_LOCK): New definition.
* elf/dl-runtime.c (_dl_fixup, _dl_profile_fixup): Or in
DL_LOOKUP_GSCOPE_LOCK into flags after THREAD_GSCOPE_SET_FLAG ().
* elf/dl-sym.c (do_sym): Likewise.
* include/link.h (struct link_map): Add l_serial field.
* elf/dl-object.c (_dl_new_object): Initialize l_serial.
* elf/dl-lookup.c (add_dependency): Add flags argument.
Remember map->l_serial, if DL_LOOKUP_GSCOPE_LOCK is among
flags, use THREAD_GSCOPE_RESET_FLAG before and
THREAD_GSCOPE_SET_FLAG after
__rtld_lock_lock_recursive (GL(dl_load_lock)) to avoid deadlock.
Don't dereference map until it has been found on some list.
If map->l_serial changed, return -1.
2007-09-17 Jakub Jelinek <jakub@redhat.com> 2007-09-17 Jakub Jelinek <jakub@redhat.com>
* include/stdio.h (__isoc99_fscanf, __isoc99_scanf, * include/stdio.h (__isoc99_fscanf, __isoc99_scanf,

View File

@ -86,21 +86,80 @@ dl_new_hash (const char *s)
/* Add extra dependency on MAP to UNDEF_MAP. */ /* Add extra dependency on MAP to UNDEF_MAP. */
static int static int
internal_function internal_function
add_dependency (struct link_map *undef_map, struct link_map *map) add_dependency (struct link_map *undef_map, struct link_map *map, int flags)
{ {
struct link_map **list; struct link_map **list;
struct link_map *runp; struct link_map *runp;
unsigned int act; unsigned int act;
unsigned int i; unsigned int i;
int result = 0; int result = 0;
unsigned long long int serial;
/* Avoid self-references and references to objects which cannot be /* Avoid self-references and references to objects which cannot be
unloaded anyway. */ unloaded anyway. */
if (undef_map == map) if (undef_map == map)
return 0; return 0;
/* Save serial number of the target MAP. */
serial = map->l_serial;
/* Make sure nobody can unload the object while we are at it. */ /* Make sure nobody can unload the object while we are at it. */
if (__builtin_expect (flags & DL_LOOKUP_GSCOPE_LOCK, 0))
{
/* We can't just call __rtld_lock_lock_recursive (GL(dl_load_lock))
here, that can result in ABBA deadlock. */
THREAD_GSCOPE_RESET_FLAG ();
__rtld_lock_lock_recursive (GL(dl_load_lock)); __rtld_lock_lock_recursive (GL(dl_load_lock));
THREAD_GSCOPE_SET_FLAG ();
/* While MAP value won't change, after THREAD_GSCOPE_RESET_FLAG ()
it can e.g. point to unallocated memory. So avoid the optimizer
treating the above read from MAP->l_serial as ensurance it
can safely dereference it. */
map = atomic_forced_read (map);
}
else
__rtld_lock_lock_recursive (GL(dl_load_lock));
/* From this point on it is unsafe to dereference MAP, until it
has been found in one of the lists. */
/* Determine whether UNDEF_MAP already has a reference to MAP. First
look in the normal dependencies. */
if (undef_map->l_initfini != NULL)
{
list = undef_map->l_initfini;
for (i = 0; list[i] != NULL; ++i)
if (list[i] == map)
goto out_check;
}
/* No normal dependency. See whether we already had to add it
to the special list of dynamic dependencies. */
list = undef_map->l_reldeps;
act = undef_map->l_reldepsact;
for (i = 0; i < act; ++i)
if (list[i] == map)
goto out_check;
/* The object is not yet in the dependency list. Before we add
it make sure just one more time the object we are about to
reference is still available. There is a brief period in
which the object could have been removed since we found the
definition. */
runp = GL(dl_ns)[undef_map->l_ns]._ns_loaded;
while (runp != NULL && runp != map)
runp = runp->l_next;
if (runp != NULL)
{
/* The object is still available. */
/* MAP could have been dlclosed, freed and then some other dlopened
library could have the same link_map pointer. */
if (map->l_serial != serial)
goto out_check;
/* Avoid references to objects which cannot be unloaded anyway. */ /* Avoid references to objects which cannot be unloaded anyway. */
if (map->l_type != lt_loaded if (map->l_type != lt_loaded
@ -117,38 +176,7 @@ add_dependency (struct link_map *undef_map, struct link_map *map)
goto out; goto out;
} }
/* Determine whether UNDEF_MAP already has a reference to MAP. First /* Add the reference now. */
look in the normal dependencies. */
if (undef_map->l_initfini != NULL)
{
list = undef_map->l_initfini;
for (i = 0; list[i] != NULL; ++i)
if (list[i] == map)
goto out;
}
/* No normal dependency. See whether we already had to add it
to the special list of dynamic dependencies. */
list = undef_map->l_reldeps;
act = undef_map->l_reldepsact;
for (i = 0; i < act; ++i)
if (list[i] == map)
goto out;
/* The object is not yet in the dependency list. Before we add
it make sure just one more time the object we are about to
reference is still available. There is a brief period in
which the object could have been removed since we found the
definition. */
runp = GL(dl_ns)[undef_map->l_ns]._ns_loaded;
while (runp != NULL && runp != map)
runp = runp->l_next;
if (runp != NULL)
{
/* The object is still available. Add the reference now. */
if (__builtin_expect (act >= undef_map->l_reldepsmax, 0)) if (__builtin_expect (act >= undef_map->l_reldepsmax, 0))
{ {
/* Allocate more memory for the dependency list. Since this /* Allocate more memory for the dependency list. Since this
@ -196,6 +224,11 @@ add_dependency (struct link_map *undef_map, struct link_map *map)
__rtld_lock_unlock_recursive (GL(dl_load_lock)); __rtld_lock_unlock_recursive (GL(dl_load_lock));
return result; return result;
out_check:
if (map->l_serial != serial)
result = -1;
goto out;
} }
static void static void
@ -227,9 +260,11 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
bump_num_relocations (); bump_num_relocations ();
/* No other flag than DL_LOOKUP_ADD_DEPENDENCY is allowed if we look /* No other flag than DL_LOOKUP_ADD_DEPENDENCY or DL_LOOKUP_GSCOPE_LOCK
up a versioned symbol. */ is allowed if we look up a versioned symbol. */
assert (version == NULL || (flags & ~(DL_LOOKUP_ADD_DEPENDENCY)) == 0); assert (version == NULL
|| (flags & ~(DL_LOOKUP_ADD_DEPENDENCY | DL_LOOKUP_GSCOPE_LOCK))
== 0);
size_t i = 0; size_t i = 0;
if (__builtin_expect (skip_map != NULL, 0)) if (__builtin_expect (skip_map != NULL, 0))
@ -335,10 +370,12 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
runtime lookups. */ runtime lookups. */
&& (flags & DL_LOOKUP_ADD_DEPENDENCY) != 0 && (flags & DL_LOOKUP_ADD_DEPENDENCY) != 0
/* Add UNDEF_MAP to the dependencies. */ /* Add UNDEF_MAP to the dependencies. */
&& add_dependency (undef_map, current_value.m) < 0) && add_dependency (undef_map, current_value.m, flags) < 0)
/* Something went wrong. Perhaps the object we tried to reference /* Something went wrong. Perhaps the object we tried to reference
was just removed. Try finding another definition. */ was just removed. Try finding another definition. */
return _dl_lookup_symbol_x (undef_name, undef_map, ref, symbol_scope, return _dl_lookup_symbol_x (undef_name, undef_map, ref,
(flags & DL_LOOKUP_GSCOPE_LOCK)
? undef_map->l_scope : symbol_scope,
version, type_class, flags, skip_map); version, type_class, flags, skip_map);
/* The object is used. */ /* The object is used. */

View File

@ -103,6 +103,7 @@ _dl_new_object (char *realname, const char *libname, int type,
else else
GL(dl_ns)[nsid]._ns_loaded = new; GL(dl_ns)[nsid]._ns_loaded = new;
++GL(dl_ns)[nsid]._ns_nloaded; ++GL(dl_ns)[nsid]._ns_nloaded;
new->l_serial = GL(dl_load_adds);
++GL(dl_load_adds); ++GL(dl_load_adds);
/* If we have no loader the new object acts as it. */ /* If we have no loader the new object acts as it. */

View File

@ -100,7 +100,10 @@ _dl_fixup (
we are not using any threads (yet). */ we are not using any threads (yet). */
int flags = DL_LOOKUP_ADD_DEPENDENCY; int flags = DL_LOOKUP_ADD_DEPENDENCY;
if (!RTLD_SINGLE_THREAD_P) if (!RTLD_SINGLE_THREAD_P)
{
THREAD_GSCOPE_SET_FLAG (); THREAD_GSCOPE_SET_FLAG ();
flags |= DL_LOOKUP_GSCOPE_LOCK;
}
result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope, result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope,
version, ELF_RTYPE_CLASS_PLT, flags, NULL); version, ELF_RTYPE_CLASS_PLT, flags, NULL);
@ -192,7 +195,10 @@ _dl_profile_fixup (
we are not using any threads (yet). */ we are not using any threads (yet). */
int flags = DL_LOOKUP_ADD_DEPENDENCY; int flags = DL_LOOKUP_ADD_DEPENDENCY;
if (!RTLD_SINGLE_THREAD_P) if (!RTLD_SINGLE_THREAD_P)
{
THREAD_GSCOPE_SET_FLAG (); THREAD_GSCOPE_SET_FLAG ();
flags |= DL_LOOKUP_GSCOPE_LOCK;
}
result = _dl_lookup_symbol_x (strtab + refsym->st_name, l, result = _dl_lookup_symbol_x (strtab + refsym->st_name, l,
&defsym, l->l_scope, version, &defsym, l->l_scope, version,

View File

@ -123,7 +123,8 @@ do_sym (void *handle, const char *name, void *who,
args.name = name; args.name = name;
args.map = match; args.map = match;
args.vers = vers; args.vers = vers;
args.flags = flags | DL_LOOKUP_ADD_DEPENDENCY; args.flags
= flags | DL_LOOKUP_ADD_DEPENDENCY | DL_LOOKUP_GSCOPE_LOCK;
args.refp = &ref; args.refp = &ref;
THREAD_GSCOPE_SET_FLAG (); THREAD_GSCOPE_SET_FLAG ();

View File

@ -286,6 +286,8 @@ struct link_map
ElfW(Addr) l_relro_addr; ElfW(Addr) l_relro_addr;
size_t l_relro_size; size_t l_relro_size;
unsigned long long int l_serial;
/* Audit information. This array apparent must be the last in the /* Audit information. This array apparent must be the last in the
structure. Never add something after it. */ structure. Never add something after it. */
struct auditstate struct auditstate

View File

@ -845,7 +845,9 @@ enum
DL_LOOKUP_ADD_DEPENDENCY = 1, DL_LOOKUP_ADD_DEPENDENCY = 1,
/* Return most recent version instead of default version for /* Return most recent version instead of default version for
unversioned lookup. */ unversioned lookup. */
DL_LOOKUP_RETURN_NEWEST = 2 DL_LOOKUP_RETURN_NEWEST = 2,
/* Set if dl_lookup* called with GSCOPE lock held. */
DL_LOOKUP_GSCOPE_LOCK = 4,
}; };
/* Lookup versioned symbol. */ /* Lookup versioned symbol. */