1
0
mirror of https://sourceware.org/git/glibc.git synced 2025-07-28 00:21:52 +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>
* 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. */
static int
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 *runp;
unsigned int act;
unsigned int i;
int result = 0;
unsigned long long int serial;
/* Avoid self-references and references to objects which cannot be
unloaded anyway. */
if (undef_map == map)
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. */
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));
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. */
if (map->l_type != lt_loaded
@ -117,38 +176,7 @@ add_dependency (struct link_map *undef_map, struct link_map *map)
goto out;
}
/* 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;
}
/* 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. */
/* Add the reference now. */
if (__builtin_expect (act >= undef_map->l_reldepsmax, 0))
{
/* 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));
return result;
out_check:
if (map->l_serial != serial)
result = -1;
goto out;
}
static void
@ -227,9 +260,11 @@ _dl_lookup_symbol_x (const char *undef_name, struct link_map *undef_map,
bump_num_relocations ();
/* No other flag than DL_LOOKUP_ADD_DEPENDENCY is allowed if we look
up a versioned symbol. */
assert (version == NULL || (flags & ~(DL_LOOKUP_ADD_DEPENDENCY)) == 0);
/* No other flag than DL_LOOKUP_ADD_DEPENDENCY or DL_LOOKUP_GSCOPE_LOCK
is allowed if we look up a versioned symbol. */
assert (version == NULL
|| (flags & ~(DL_LOOKUP_ADD_DEPENDENCY | DL_LOOKUP_GSCOPE_LOCK))
== 0);
size_t i = 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. */
&& (flags & DL_LOOKUP_ADD_DEPENDENCY) != 0
/* 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
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);
/* The object is used. */

View File

@ -103,6 +103,7 @@ _dl_new_object (char *realname, const char *libname, int type,
else
GL(dl_ns)[nsid]._ns_loaded = new;
++GL(dl_ns)[nsid]._ns_nloaded;
new->l_serial = GL(dl_load_adds);
++GL(dl_load_adds);
/* 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). */
int flags = DL_LOOKUP_ADD_DEPENDENCY;
if (!RTLD_SINGLE_THREAD_P)
{
THREAD_GSCOPE_SET_FLAG ();
flags |= DL_LOOKUP_GSCOPE_LOCK;
}
result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope,
version, ELF_RTYPE_CLASS_PLT, flags, NULL);
@ -192,7 +195,10 @@ _dl_profile_fixup (
we are not using any threads (yet). */
int flags = DL_LOOKUP_ADD_DEPENDENCY;
if (!RTLD_SINGLE_THREAD_P)
{
THREAD_GSCOPE_SET_FLAG ();
flags |= DL_LOOKUP_GSCOPE_LOCK;
}
result = _dl_lookup_symbol_x (strtab + refsym->st_name, l,
&defsym, l->l_scope, version,

View File

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

View File

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

View File

@ -845,7 +845,9 @@ enum
DL_LOOKUP_ADD_DEPENDENCY = 1,
/* Return most recent version instead of default version for
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. */