import glibc-2.28-101.el8.src.rpm
Signed-off-by: zhangbinchen <zhangbinchen@openanolis.org>
This commit is contained in:
commit
ba6a26df65
268 changed files with 59851 additions and 0 deletions
308
glibc-rh1410154-6.patch
Normal file
308
glibc-rh1410154-6.patch
Normal file
|
@ -0,0 +1,308 @@
|
|||
commit 440b7f8653e4ed8f6e1425145208050b795e9a6c
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Thu Oct 31 18:25:39 2019 +0100
|
||||
|
||||
Avoid late failure in dlopen in global scope update [BZ #25112]
|
||||
|
||||
The call to add_to_global in dl_open_worker happens after running ELF
|
||||
constructors for new objects. At this point, proper recovery from
|
||||
malloc failure would be quite complicated: We would have to run the
|
||||
ELF destructors and close all opened objects, something that we
|
||||
currently do not do.
|
||||
|
||||
Instead, this change splits add_to_global into two phases,
|
||||
add_to_global_resize (which can raise an exception, called before ELF
|
||||
constructors run), and add_to_global_update (which cannot, called
|
||||
after ELF constructors). A complication arises due to recursive
|
||||
dlopen: After the inner dlopen consumes some space, the pre-allocation
|
||||
in the outer dlopen may no longer be sufficient. A new member in the
|
||||
namespace structure, _ns_global_scope_pending_adds keeps track of the
|
||||
maximum number of objects that need to be added to the global scope.
|
||||
This enables the inner add_to_global_resize call to take into account
|
||||
the needs of an outer dlopen.
|
||||
|
||||
Most code in the dynamic linker assumes that the number of global
|
||||
scope entries fits into an unsigned int (matching the r_nlist member
|
||||
of struct r_scop_elem). Therefore, change the type of
|
||||
_ns_global_scope_alloc to unsigned int (from size_t), and add overflow
|
||||
checks.
|
||||
|
||||
Change-Id: Ie08e2f318510d5a6a4bcb1c315f46791b5b77524
|
||||
|
||||
diff --git a/elf/dl-open.c b/elf/dl-open.c
|
||||
index c9c0254ee74c4f4b..85db4f0ecb5f29ce 100644
|
||||
--- a/elf/dl-open.c
|
||||
+++ b/elf/dl-open.c
|
||||
@@ -50,22 +50,38 @@ struct dl_open_args
|
||||
struct link_map *map;
|
||||
/* Namespace ID. */
|
||||
Lmid_t nsid;
|
||||
+
|
||||
+ /* Original value of _ns_global_scope_pending_adds. Set by
|
||||
+ dl_open_worker. Only valid if nsid is a real namespace
|
||||
+ (non-negative). */
|
||||
+ unsigned int original_global_scope_pending_adds;
|
||||
+
|
||||
/* Original parameters to the program and the current environment. */
|
||||
int argc;
|
||||
char **argv;
|
||||
char **env;
|
||||
};
|
||||
|
||||
+/* Called in case the global scope cannot be extended. */
|
||||
+static void __attribute__ ((noreturn))
|
||||
+add_to_global_resize_failure (struct link_map *new)
|
||||
+{
|
||||
+ _dl_signal_error (ENOMEM, new->l_libname->name, NULL,
|
||||
+ N_ ("cannot extend global scope"));
|
||||
+}
|
||||
|
||||
-static int
|
||||
-add_to_global (struct link_map *new)
|
||||
+/* Grow the global scope array for the namespace, so that all the new
|
||||
+ global objects can be added later in add_to_global_update, without
|
||||
+ risk of memory allocation failure. add_to_global_resize raises
|
||||
+ exceptions for memory allocation errors. */
|
||||
+static void
|
||||
+add_to_global_resize (struct link_map *new)
|
||||
{
|
||||
- struct link_map **new_global;
|
||||
- unsigned int to_add = 0;
|
||||
- unsigned int cnt;
|
||||
+ struct link_namespaces *ns = &GL (dl_ns)[new->l_ns];
|
||||
|
||||
/* Count the objects we have to put in the global scope. */
|
||||
- for (cnt = 0; cnt < new->l_searchlist.r_nlist; ++cnt)
|
||||
+ unsigned int to_add = 0;
|
||||
+ for (unsigned int cnt = 0; cnt < new->l_searchlist.r_nlist; ++cnt)
|
||||
if (new->l_searchlist.r_list[cnt]->l_global == 0)
|
||||
++to_add;
|
||||
|
||||
@@ -83,47 +99,51 @@ add_to_global (struct link_map *new)
|
||||
in an realloc() call. Therefore we allocate a completely new
|
||||
array the first time we have to add something to the locale scope. */
|
||||
|
||||
- struct link_namespaces *ns = &GL(dl_ns)[new->l_ns];
|
||||
+ if (__builtin_add_overflow (ns->_ns_global_scope_pending_adds, to_add,
|
||||
+ &ns->_ns_global_scope_pending_adds))
|
||||
+ add_to_global_resize_failure (new);
|
||||
+
|
||||
+ unsigned int new_size = 0; /* 0 means no new allocation. */
|
||||
+ void *old_global = NULL; /* Old allocation if free-able. */
|
||||
+
|
||||
+ /* Minimum required element count for resizing. Adjusted below for
|
||||
+ an exponential resizing policy. */
|
||||
+ size_t required_new_size;
|
||||
+ if (__builtin_add_overflow (ns->_ns_main_searchlist->r_nlist,
|
||||
+ ns->_ns_global_scope_pending_adds,
|
||||
+ &required_new_size))
|
||||
+ add_to_global_resize_failure (new);
|
||||
+
|
||||
if (ns->_ns_global_scope_alloc == 0)
|
||||
{
|
||||
- /* This is the first dynamic object given global scope. */
|
||||
- ns->_ns_global_scope_alloc
|
||||
- = ns->_ns_main_searchlist->r_nlist + to_add + 8;
|
||||
- new_global = (struct link_map **)
|
||||
- malloc (ns->_ns_global_scope_alloc * sizeof (struct link_map *));
|
||||
- if (new_global == NULL)
|
||||
- {
|
||||
- ns->_ns_global_scope_alloc = 0;
|
||||
- nomem:
|
||||
- _dl_signal_error (ENOMEM, new->l_libname->name, NULL,
|
||||
- N_("cannot extend global scope"));
|
||||
- return 1;
|
||||
- }
|
||||
+ if (__builtin_add_overflow (required_new_size, 8, &new_size))
|
||||
+ add_to_global_resize_failure (new);
|
||||
+ }
|
||||
+ else if (required_new_size > ns->_ns_global_scope_alloc)
|
||||
+ {
|
||||
+ if (__builtin_mul_overflow (required_new_size, 2, &new_size))
|
||||
+ add_to_global_resize_failure (new);
|
||||
|
||||
- /* Copy over the old entries. */
|
||||
- ns->_ns_main_searchlist->r_list
|
||||
- = memcpy (new_global, ns->_ns_main_searchlist->r_list,
|
||||
- (ns->_ns_main_searchlist->r_nlist
|
||||
- * sizeof (struct link_map *)));
|
||||
+ /* The old array was allocated with our malloc, not the minimal
|
||||
+ malloc. */
|
||||
+ old_global = ns->_ns_main_searchlist->r_list;
|
||||
}
|
||||
- else if (ns->_ns_main_searchlist->r_nlist + to_add
|
||||
- > ns->_ns_global_scope_alloc)
|
||||
+
|
||||
+ if (new_size > 0)
|
||||
{
|
||||
- /* We have to extend the existing array of link maps in the
|
||||
- main map. */
|
||||
- struct link_map **old_global
|
||||
- = GL(dl_ns)[new->l_ns]._ns_main_searchlist->r_list;
|
||||
- size_t new_nalloc = ((ns->_ns_global_scope_alloc + to_add) * 2);
|
||||
-
|
||||
- new_global = (struct link_map **)
|
||||
- malloc (new_nalloc * sizeof (struct link_map *));
|
||||
+ size_t allocation_size;
|
||||
+ if (__builtin_mul_overflow (new_size, sizeof (struct link_map *),
|
||||
+ &allocation_size))
|
||||
+ add_to_global_resize_failure (new);
|
||||
+ struct link_map **new_global = malloc (allocation_size);
|
||||
if (new_global == NULL)
|
||||
- goto nomem;
|
||||
+ add_to_global_resize_failure (new);
|
||||
|
||||
- memcpy (new_global, old_global,
|
||||
- ns->_ns_global_scope_alloc * sizeof (struct link_map *));
|
||||
+ /* Copy over the old entries. */
|
||||
+ memcpy (new_global, ns->_ns_main_searchlist->r_list,
|
||||
+ ns->_ns_main_searchlist->r_nlist * sizeof (struct link_map *));
|
||||
|
||||
- ns->_ns_global_scope_alloc = new_nalloc;
|
||||
+ ns->_ns_global_scope_alloc = new_size;
|
||||
ns->_ns_main_searchlist->r_list = new_global;
|
||||
|
||||
if (!RTLD_SINGLE_THREAD_P)
|
||||
@@ -131,16 +151,28 @@ add_to_global (struct link_map *new)
|
||||
|
||||
free (old_global);
|
||||
}
|
||||
+}
|
||||
+
|
||||
+/* Actually add the new global objects to the global scope. Must be
|
||||
+ called after add_to_global_resize. This function cannot fail. */
|
||||
+static void
|
||||
+add_to_global_update (struct link_map *new)
|
||||
+{
|
||||
+ struct link_namespaces *ns = &GL (dl_ns)[new->l_ns];
|
||||
|
||||
/* Now add the new entries. */
|
||||
unsigned int new_nlist = ns->_ns_main_searchlist->r_nlist;
|
||||
- for (cnt = 0; cnt < new->l_searchlist.r_nlist; ++cnt)
|
||||
+ for (unsigned int cnt = 0; cnt < new->l_searchlist.r_nlist; ++cnt)
|
||||
{
|
||||
struct link_map *map = new->l_searchlist.r_list[cnt];
|
||||
|
||||
if (map->l_global == 0)
|
||||
{
|
||||
map->l_global = 1;
|
||||
+
|
||||
+ /* The array has been resized by add_to_global_resize. */
|
||||
+ assert (new_nlist < ns->_ns_global_scope_alloc);
|
||||
+
|
||||
ns->_ns_main_searchlist->r_list[new_nlist++] = map;
|
||||
|
||||
/* We modify the global scope. Report this. */
|
||||
@@ -149,10 +181,15 @@ add_to_global (struct link_map *new)
|
||||
map->l_name, map->l_ns);
|
||||
}
|
||||
}
|
||||
+
|
||||
+ /* Some of the pending adds have been performed by the loop above.
|
||||
+ Adjust the counter accordingly. */
|
||||
+ unsigned int added = new_nlist - ns->_ns_main_searchlist->r_nlist;
|
||||
+ assert (added <= ns->_ns_global_scope_pending_adds);
|
||||
+ ns->_ns_global_scope_pending_adds -= added;
|
||||
+
|
||||
atomic_write_barrier ();
|
||||
ns->_ns_main_searchlist->r_nlist = new_nlist;
|
||||
-
|
||||
- return 0;
|
||||
}
|
||||
|
||||
/* Search link maps in all namespaces for the DSO that contains the object at
|
||||
@@ -225,6 +262,10 @@ dl_open_worker (void *a)
|
||||
args->nsid = call_map->l_ns;
|
||||
}
|
||||
|
||||
+ /* Retain the old value, so that it can be restored. */
|
||||
+ args->original_global_scope_pending_adds
|
||||
+ = GL (dl_ns)[args->nsid]._ns_global_scope_pending_adds;
|
||||
+
|
||||
/* 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);
|
||||
@@ -266,7 +307,10 @@ dl_open_worker (void *a)
|
||||
/* If the user requested the object to be in the global namespace
|
||||
but it is not so far, add it now. */
|
||||
if ((mode & RTLD_GLOBAL) && new->l_global == 0)
|
||||
- (void) add_to_global (new);
|
||||
+ {
|
||||
+ add_to_global_resize (new);
|
||||
+ add_to_global_update (new);
|
||||
+ }
|
||||
|
||||
assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
|
||||
|
||||
@@ -523,6 +567,11 @@ TLS generation counter wrapped! Please report this."));
|
||||
DL_STATIC_INIT (new);
|
||||
#endif
|
||||
|
||||
+ /* Perform the necessary allocations for adding new global objects
|
||||
+ to the global scope below, via add_to_global_update. */
|
||||
+ if (mode & RTLD_GLOBAL)
|
||||
+ add_to_global_resize (new);
|
||||
+
|
||||
/* Run the initializer functions of new objects. Temporarily
|
||||
disable the exception handler, so that lazy binding failures are
|
||||
fatal. */
|
||||
@@ -539,10 +588,7 @@ TLS generation counter wrapped! Please report this."));
|
||||
|
||||
/* Now we can make the new map available in the global scope. */
|
||||
if (mode & RTLD_GLOBAL)
|
||||
- /* Move the object in the global namespace. */
|
||||
- if (add_to_global (new) != 0)
|
||||
- /* It failed. */
|
||||
- return;
|
||||
+ add_to_global_update (new);
|
||||
|
||||
#ifndef SHARED
|
||||
/* We must be the static _dl_open in libc.a. A static program that
|
||||
@@ -556,7 +602,6 @@ TLS generation counter wrapped! Please report this."));
|
||||
new->l_name, new->l_ns, new->l_direct_opencount);
|
||||
}
|
||||
|
||||
-
|
||||
void *
|
||||
_dl_open (const char *file, int mode, const void *caller_dlopen, Lmid_t nsid,
|
||||
int argc, char *argv[], char *env[])
|
||||
@@ -624,6 +669,19 @@ no more namespaces available for dlmopen()"));
|
||||
_dl_unload_cache ();
|
||||
#endif
|
||||
|
||||
+ /* Do this for both the error and success cases. The old value has
|
||||
+ only been determined if the namespace ID was assigned (i.e., it
|
||||
+ is not __LM_ID_CALLER). In the success case, we actually may
|
||||
+ have consumed more pending adds than planned (because the local
|
||||
+ scopes overlap in case of a recursive dlopen, the inner dlopen
|
||||
+ doing some of the globalization work of the outer dlopen), so the
|
||||
+ old pending adds value is larger than absolutely necessary.
|
||||
+ Since it is just a conservative upper bound, this is harmless.
|
||||
+ The top-level dlopen call will restore the field to zero. */
|
||||
+ if (args.nsid >= 0)
|
||||
+ GL (dl_ns)[args.nsid]._ns_global_scope_pending_adds
|
||||
+ = args.original_global_scope_pending_adds;
|
||||
+
|
||||
/* See if an error occurred during loading. */
|
||||
if (__glibc_unlikely (exception.errstring != NULL))
|
||||
{
|
||||
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
|
||||
index 6c5298a80bff8e96..57fbefea3cb841e9 100644
|
||||
--- a/sysdeps/generic/ldsodefs.h
|
||||
+++ b/sysdeps/generic/ldsodefs.h
|
||||
@@ -311,7 +311,14 @@ struct rtld_global
|
||||
/* This is zero at program start to signal that the global scope map is
|
||||
allocated by rtld. Later it keeps the size of the map. It might be
|
||||
reset if in _dl_close if the last global object is removed. */
|
||||
- size_t _ns_global_scope_alloc;
|
||||
+ unsigned int _ns_global_scope_alloc;
|
||||
+
|
||||
+ /* During dlopen, this is the number of objects that still need to
|
||||
+ be added to the global scope map. It has to be taken into
|
||||
+ account when resizing the map, for future map additions after
|
||||
+ recursive dlopen calls from ELF constructors. */
|
||||
+ unsigned int _ns_global_scope_pending_adds;
|
||||
+
|
||||
/* Search table for unique objects. */
|
||||
struct unique_sym_table
|
||||
{
|
Loading…
Add table
Add a link
Reference in a new issue