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:
16
ChangeLog
16
ChangeLog
@ -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,
|
||||||
|
113
elf/dl-lookup.c
113
elf/dl-lookup.c
@ -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. */
|
||||||
|
@ -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. */
|
||||||
|
@ -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,
|
||||||
|
@ -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 ();
|
||||||
|
@ -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
|
||||||
|
@ -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. */
|
||||||
|
Reference in New Issue
Block a user