[3/3] elf: Switch to main malloc after final ld.so self-relocation

Message ID 61844731147e785796f487d75519012bf50124c2.1722934012.git.fweimer@redhat.com
State New
Headers
Series Make ld.so self-relocation/main malloc switch more robust |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
redhat-pt-bot/TryBot-32bit success Build for i686
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-arm success Test passed

Commit Message

Florian Weimer Aug. 6, 2024, 8:48 a.m. UTC
  Before commit ee1ada1bdb8074de6e1bdc956ab19aef7b6a7872
("elf: Rework exception handling in the dynamic loader
[BZ #25486]"), the previous order called the main calloc
to allocate a shadow GOT/PLT array for auditing support.
This happened before libc.so.6 ELF constructors were run, so
a user malloc could run without libc.so.6 having been
initialized fully.  One observable effect was that
environ was NULL at this point.

It does not seem to be possible at present to trigger such
an allocation, but it seems more robust to delay switching
to main malloc after ld.so self-relocation is complete.
The elf/tst-rtld-no-malloc-audit test case fails with a
2.34-era glibc that does not have this fix.
---
 elf/Makefile                     |  9 ++++
 elf/dl-support.c                 |  3 +-
 elf/rtld.c                       | 26 +++++------
 elf/tst-rtld-no-malloc-audit.c   |  1 +
 elf/tst-rtld-no-malloc-preload.c |  1 +
 elf/tst-rtld-no-malloc.c         | 75 ++++++++++++++++++++++++++++++++
 6 files changed, 99 insertions(+), 16 deletions(-)
 create mode 100644 elf/tst-rtld-no-malloc-audit.c
 create mode 100644 elf/tst-rtld-no-malloc-preload.c
 create mode 100644 elf/tst-rtld-no-malloc.c
  

Patch

diff --git a/elf/Makefile b/elf/Makefile
index a03c6520d8..f34febfa5b 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -452,6 +452,9 @@  tests += \
   tst-recursive-tls \
   tst-relsort1 \
   tst-ro-dynamic \
+  tst-rtld-no-malloc \
+  tst-rtld-no-malloc-audit \
+  tst-rtld-no-malloc-preload \
   tst-rtld-run-static \
   tst-single_threaded \
   tst-single_threaded-pthread \
@@ -3148,3 +3151,9 @@  $(objpfx)tst-dlopen-tlsreinit3.out: $(objpfx)tst-auditmod1.so
 tst-dlopen-tlsreinit3-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
 $(objpfx)tst-dlopen-tlsreinit4.out: $(objpfx)tst-auditmod1.so
 tst-dlopen-tlsreinit4-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
+
+# Reuse an audit module which provides ample debug logging.
+tst-rtld-no-malloc-audit-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
+
+# Any shared object should do.
+tst-rtld-no-malloc-preload-ENV = LD_PRELOAD=$(objpfx)tst-auditmod1.so
diff --git a/elf/dl-support.c b/elf/dl-support.c
index 451932dd03..ee590edf93 100644
--- a/elf/dl-support.c
+++ b/elf/dl-support.c
@@ -338,8 +338,7 @@  _dl_non_dynamic_init (void)
   call_function_static_weak (_dl_find_object_init);
 
   /* Setup relro on the binary itself.  */
-  if (_dl_main_map.l_relro_size != 0)
-    _dl_protect_relro (&_dl_main_map);
+  _dl_protect_relro (&_dl_main_map);
 }
 
 #ifdef DL_SYSINFO_IMPLEMENTATION
diff --git a/elf/rtld.c b/elf/rtld.c
index cc4f3e51f9..1e6c506634 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -2341,34 +2341,32 @@  dl_main (const ElfW(Phdr) *phdr,
   /* Make sure no new search directories have been added.  */
   assert (GLRO(dl_init_all_dirs) == GL(dl_all_dirs));
 
-  /* Re-relocate ourselves with user-controlled symbol definitions.
-
-     We must do this after TLS initialization in case after this
-     re-relocation, we might call a user-supplied function
-     (e.g. calloc from _dl_relocate_object) that uses TLS data.  */
-
   /* Set up the object lookup structures.  */
   _dl_find_object_init ();
 
-  /* 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);
-
   /* Likewise for the locking implementation.  */
   __rtld_mutex_init ();
 
+  /* Re-relocate ourselves with user-controlled symbol definitions.  */
+
   {
     RTLD_TIMING_VAR (start);
     rtld_timer_start (&start);
 
-    /* Mark the link map as not yet relocated again.  */
-    GL(dl_rtld_map).l_relocated = 0;
-    _dl_relocate_object (&GL(dl_rtld_map), main_map->l_scope, 0, 0);
+    _dl_relocate_object_no_relro (&GL(dl_rtld_map), main_map->l_scope, 0, 0);
+
+    /* 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);
+
+    if (GL(dl_rtld_map).l_relro_size != 0)
+      _dl_protect_relro (&GL(dl_rtld_map));
 
     rtld_timer_accum (&relocate_time, start);
   }
 
+
   /* Relocation is complete.  Perform early libc initialization.  This
      is the initial libc, even if audit modules have been loaded with
      other libcs.  */
diff --git a/elf/tst-rtld-no-malloc-audit.c b/elf/tst-rtld-no-malloc-audit.c
new file mode 100644
index 0000000000..a028377ad1
--- /dev/null
+++ b/elf/tst-rtld-no-malloc-audit.c
@@ -0,0 +1 @@ 
+#include "tst-rtld-no-malloc.c"
diff --git a/elf/tst-rtld-no-malloc-preload.c b/elf/tst-rtld-no-malloc-preload.c
new file mode 100644
index 0000000000..a028377ad1
--- /dev/null
+++ b/elf/tst-rtld-no-malloc-preload.c
@@ -0,0 +1 @@ 
+#include "tst-rtld-no-malloc.c"
diff --git a/elf/tst-rtld-no-malloc.c b/elf/tst-rtld-no-malloc.c
new file mode 100644
index 0000000000..a78f1ba727
--- /dev/null
+++ b/elf/tst-rtld-no-malloc.c
@@ -0,0 +1,75 @@ 
+/* Test that program loading does not call malloc.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+
+#include <string.h>
+#include <unistd.h>
+
+static void
+print (const char *s)
+{
+  const char *end = s + strlen (s);
+  while (s < end)
+    {
+      ssize_t ret = write (STDOUT_FILENO, s, end - s);
+      if (ret <= 0)
+        _exit (2);
+      s += ret;
+    }
+}
+
+static void __attribute__ ((noreturn))
+unexpected_call (const char *function)
+{
+  print ("error: unexpected call to ");
+  print (function);
+  print ("\n");
+  _exit (1);
+}
+
+/* These are the malloc functions implement in elf/dl-minimal.c.  */
+
+void
+free (void *ignored)
+{
+  unexpected_call ("free");
+}
+
+void *
+calloc (size_t ignored1, size_t ignored2)
+{
+  unexpected_call ("calloc");
+}
+
+void *
+malloc (size_t ignored)
+{
+  unexpected_call ("malloc");
+}
+
+void *
+realloc (void *ignored1, size_t ignored2)
+{
+  unexpected_call ("realloc");
+}
+
+int
+main (void)
+{
+  /* Do not use the test wrapper, to avoid spurious malloc calls from it.  */
+}