summaryrefslogtreecommitdiff
path: root/elf
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2020-02-08 19:58:43 +0100
committerFlorian Weimer <fweimer@redhat.com>2020-02-15 11:01:23 +0100
commit3a0ecccb599a6b1ad4b149dc569c0080e92d057b (patch)
treee1c4c0e5f2e80221054d6bb6260b4038e27567b4 /elf
parent2efa52c880d46ee89523c8ed8102ceeb02043926 (diff)
downloadglibc-3a0ecccb599a6b1ad4b149dc569c0080e92d057b.tar.gz
ld.so: Do not export free/calloc/malloc/realloc functions [BZ #25486]
Exporting functions and relying on symbol interposition from libc.so makes the choice of implementation dependent on DT_NEEDED order, which is not what some compiler drivers expect. This commit replaces one magic mechanism (symbol interposition) with another one (preprocessor-/compiler-based redirection). This makes the hand-over from the minimal malloc to the full malloc more explicit. Removing the ABI symbols is backwards-compatible because libc.so is always in scope, and the dynamic loader will find the malloc-related symbols there since commit f0b2132b35248c1f4a80f62a2c38cddcc802aa8c ("ld.so: Support moving versioned symbols between sonames [BZ #24741]"). Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Diffstat (limited to 'elf')
-rw-r--r--elf/Makefile6
-rw-r--r--elf/Versions3
-rw-r--r--elf/dl-lookup.c4
-rw-r--r--elf/dl-minimal.c97
-rw-r--r--elf/rtld.c12
5 files changed, 103 insertions, 19 deletions
diff --git a/elf/Makefile b/elf/Makefile
index ffc34a67d4..a137143db7 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -488,7 +488,11 @@ $(objpfx)dl-allobjs.os: $(all-rtld-routines:%=$(objpfx)%.os)
# their implementation is provided differently in rtld, and the symbol
# discovery mechanism is not compatible with the libc implementation
# when compiled for libc.
-rtld-stubbed-symbols =
+rtld-stubbed-symbols = \
+ calloc \
+ free \
+ malloc \
+ realloc \
# The GCC arguments that implement $(rtld-stubbed-symbols).
rtld-stubbed-symbols-args = \
diff --git a/elf/Versions b/elf/Versions
index 3b09901f6c..705489fc51 100644
--- a/elf/Versions
+++ b/elf/Versions
@@ -35,9 +35,6 @@ libc {
ld {
GLIBC_2.0 {
- # Functions which are interposed from libc.so.
- calloc; free; malloc; realloc;
-
_r_debug;
}
GLIBC_2.1 {
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
index 378f28fa7d..12a229f06c 100644
--- a/elf/dl-lookup.c
+++ b/elf/dl-lookup.c
@@ -291,7 +291,7 @@ do_lookup_unique (const char *undef_name, uint_fast32_t new_hash,
tab->size = newsize;
size = newsize;
entries = tab->entries = newentries;
- tab->free = free;
+ tab->free = __rtld_free;
}
}
else
@@ -322,7 +322,7 @@ do_lookup_unique (const char *undef_name, uint_fast32_t new_hash,
tab->entries = entries;
tab->size = size;
- tab->free = free;
+ tab->free = __rtld_free;
}
if ((type_class & ELF_RTYPE_CLASS_COPY) != 0)
diff --git a/elf/dl-minimal.c b/elf/dl-minimal.c
index 42192f8a7b..c79ce23be4 100644
--- a/elf/dl-minimal.c
+++ b/elf/dl-minimal.c
@@ -26,11 +26,87 @@
#include <sys/param.h>
#include <sys/types.h>
#include <ldsodefs.h>
+#include <dl-irel.h>
+#include <dl-hash.h>
+#include <dl-sym-post.h>
#include <_itoa.h>
#include <malloc/malloc-internal.h>
#include <assert.h>
+/* The rtld startup code calls __rtld_malloc_init_stubs after the
+ first self-relocation to adjust the pointers to the minimal
+ implementation below. Before the final relocation,
+ __rtld_malloc_init_real is called to replace the pointers with the
+ real implementation. */
+__typeof (calloc) *__rtld_calloc;
+__typeof (free) *__rtld_free;
+__typeof (malloc) *__rtld_malloc;
+__typeof (realloc) *__rtld_realloc;
+
+/* Defined below. */
+static __typeof (calloc) rtld_calloc attribute_relro;
+static __typeof (free) rtld_free attribute_relro;
+static __typeof (malloc) rtld_malloc attribute_relro;
+static __typeof (realloc) rtld_realloc attribute_relro;
+
+void
+__rtld_malloc_init_stubs (void)
+{
+ __rtld_calloc = &rtld_calloc;
+ __rtld_free = &rtld_free;
+ __rtld_malloc = &rtld_malloc;
+ __rtld_realloc = &rtld_realloc;
+}
+
+/* Lookup NAME at VERSION in the scope of MATCH. */
+static void *
+lookup_malloc_symbol (struct link_map *main_map, const char *name,
+ struct r_found_version *version)
+{
+
+ const ElfW(Sym) *ref = NULL;
+ lookup_t result = _dl_lookup_symbol_x (name, main_map, &ref,
+ main_map->l_scope,
+ version, 0, 0, NULL);
+
+ assert (ELFW(ST_TYPE) (ref->st_info) != STT_TLS);
+ void *value = DL_SYMBOL_ADDRESS (result, ref);
+
+ return _dl_sym_post (result, ref, value, 0, main_map);
+}
+
+void
+__rtld_malloc_init_real (struct link_map *main_map)
+{
+ /* We cannot use relocations and initializers for this because the
+ changes made by __rtld_malloc_init_stubs break REL-style
+ (non-RELA) relocations that depend on the previous pointer
+ contents. Also avoid direct relocation depedencies for the
+ malloc symbols so this function can be called before the final
+ rtld relocation (which enables RELRO, after which the pointer
+ variables cannot be written to). */
+
+ struct r_found_version version;
+ version.name = symbol_version_string (libc, GLIBC_2_0);
+ version.hidden = 0;
+ version.hash = _dl_elf_hash (version.name);
+ version.filename = NULL;
+
+ void *new_calloc = lookup_malloc_symbol (main_map, "calloc", &version);
+ void *new_free = lookup_malloc_symbol (main_map, "free", &version);
+ void *new_malloc = lookup_malloc_symbol (main_map, "malloc", &version);
+ void *new_realloc = lookup_malloc_symbol (main_map, "realloc", &version);
+
+ /* Update the pointers in one go, so that any internal allocations
+ performed by lookup_malloc_symbol see a consistent
+ implementation. */
+ __rtld_calloc = new_calloc;
+ __rtld_free = new_free;
+ __rtld_malloc = new_malloc;
+ __rtld_realloc = new_realloc;
+}
+
/* Minimal malloc allocator for used during initial link. After the
initial link, a full malloc implementation is interposed, either
the one in libc, or a different one supplied by the user through
@@ -38,14 +114,9 @@
static void *alloc_ptr, *alloc_end, *alloc_last_block;
-/* Declarations of global functions. */
-extern void weak_function free (void *ptr);
-extern void * weak_function realloc (void *ptr, size_t n);
-
-
/* Allocate an aligned memory block. */
-void * weak_function
-malloc (size_t n)
+static void *
+rtld_malloc (size_t n)
{
if (alloc_end == 0)
{
@@ -87,8 +158,8 @@ malloc (size_t n)
/* We use this function occasionally since the real implementation may
be optimized when it can assume the memory it returns already is
set to NUL. */
-void * weak_function
-calloc (size_t nmemb, size_t size)
+static void *
+rtld_calloc (size_t nmemb, size_t size)
{
/* New memory from the trivial malloc above is always already cleared.
(We make sure that's true in the rare occasion it might not be,
@@ -104,8 +175,8 @@ calloc (size_t nmemb, size_t size)
}
/* This will rarely be called. */
-void weak_function
-free (void *ptr)
+void
+rtld_free (void *ptr)
{
/* We can free only the last block allocated. */
if (ptr == alloc_last_block)
@@ -118,8 +189,8 @@ free (void *ptr)
}
/* This is only called with the most recent block returned by malloc. */
-void * weak_function
-realloc (void *ptr, size_t n)
+void *
+rtld_realloc (void *ptr, size_t n)
{
if (ptr == NULL)
return malloc (n);
diff --git a/elf/rtld.c b/elf/rtld.c
index 553cfbd1b7..51dfaf966a 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -534,6 +534,9 @@ _dl_start (void *arg)
header table in core. Put the rest of _dl_start into a separate
function, that way the compiler cannot put accesses to the GOT
before ELF_DYNAMIC_RELOCATE. */
+
+ __rtld_malloc_init_stubs ();
+
{
#ifdef DONT_USE_BOOTSTRAP_MAP
ElfW(Addr) entry = _dl_start_final (arg);
@@ -2210,6 +2213,10 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
rtld_timer_stop (&relocate_time, start);
}
+ /* The library defining malloc has already been relocated due to
+ prelinking. Resolve the malloc symbols for the dynamic
+ loader. */
+ __rtld_malloc_init_real (main_map);
/* Mark all the objects so we know they have been already relocated. */
for (struct link_map *l = main_map; l != NULL; l = l->l_next)
@@ -2310,6 +2317,11 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
re-relocation, we might call a user-supplied function
(e.g. calloc from _dl_relocate_object) that uses TLS data. */
+ /* The malloc implementation has been relocated, so resolving
+ its symbols (and potentially calling IFUNC resolvers) is safe
+ at this point. */
+ __rtld_malloc_init_real (main_map);
+
RTLD_TIMING_VAR (start);
rtld_timer_start (&start);