about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--NEWS6
-rw-r--r--elf/Makefile15
-rw-r--r--elf/dl-close.c46
-rw-r--r--elf/dl-open.c32
-rw-r--r--elf/tst-finilazyfailmod.c27
-rw-r--r--elf/tst-initfinilazyfail.c84
-rw-r--r--elf/tst-initlazyfailmod.c27
7 files changed, 216 insertions, 21 deletions
diff --git a/NEWS b/NEWS
index 12b239c1fb..5a2d0a5b8b 100644
--- a/NEWS
+++ b/NEWS
@@ -93,6 +93,12 @@ Deprecated and removed features, and other changes affecting compatibility:
   are no longer supported.  For v8 only implementations with native CAS
   instruction are still supported (such as LEON).
 
+* If a lazy binding failure happens during dlopen, during the execution of
+  an ELF constructor, the process is now terminated.  Previously, the
+  dynamic loader would return NULL from dlopen, with the lazy binding error
+  captured in a dlerror message.  In general, this is unsafe because
+  resetting the stack in an arbitrary function call is not possible.
+
 Changes to build and runtime requirements:
 
   [Add changes to build and runtime requirements here]
diff --git a/elf/Makefile b/elf/Makefile
index b05af5ce3a..30c389657b 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -199,7 +199,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
 	 tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \
 	 tst-unwind-ctor tst-unwind-main tst-audit13 \
 	 tst-sonamemove-link tst-sonamemove-dlopen tst-dlopen-tlsmodid \
-	 tst-dlopen-self tst-auditmany
+	 tst-dlopen-self tst-auditmany tst-initfinilazyfail
 #	 reldep9
 tests-internal += loadtest unload unload2 circleload1 \
 	 neededtest neededtest2 neededtest3 neededtest4 \
@@ -290,7 +290,8 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
 		tst-sonamemove-runmod1 tst-sonamemove-runmod2 \
 		tst-auditmanymod1 tst-auditmanymod2 tst-auditmanymod3 \
 		tst-auditmanymod4 tst-auditmanymod5 tst-auditmanymod6 \
-		tst-auditmanymod7 tst-auditmanymod8 tst-auditmanymod9
+		tst-auditmanymod7 tst-auditmanymod8 tst-auditmanymod9 \
+		tst-initlazyfailmod tst-finilazyfailmod
 # Most modules build with _ISOMAC defined, but those filtered out
 # depend on internal headers.
 modules-names-tests = $(filter-out ifuncmod% tst-libc_dlvsym-dso tst-tlsmod%,\
@@ -1586,3 +1587,13 @@ $(objpfx)tst-big-note-lib.so: $(objpfx)tst-big-note-lib.o
 $(objpfx)tst-unwind-ctor: $(objpfx)tst-unwind-ctor-lib.so
 
 CFLAGS-tst-unwind-main.c += -funwind-tables -DUSE_PTHREADS=0
+
+$(objpfx)tst-initfinilazyfail: $(libdl)
+$(objpfx)tst-initfinilazyfail.out: \
+  $(objpfx)tst-initlazyfailmod.so $(objpfx)tst-finilazyfailmod.so
+# Override -z defs, so that we can reference an undefined symbol.
+# Force lazy binding for the same reason.
+LDFLAGS-tst-initlazyfailmod.so = \
+  -Wl,-z,lazy -Wl,--unresolved-symbols=ignore-all
+LDFLAGS-tst-finilazyfailmod.so = \
+  -Wl,-z,lazy -Wl,--unresolved-symbols=ignore-all
diff --git a/elf/dl-close.c b/elf/dl-close.c
index c32e6473ad..33486b9cbc 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -106,6 +106,30 @@ remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp,
   return false;
 }
 
+/* Invoke dstructors for CLOSURE (a struct link_map *).  Called with
+   exception handling temporarily disabled, to make errors fatal.  */
+static void
+call_destructors (void *closure)
+{
+  struct link_map *map = closure;
+
+  if (map->l_info[DT_FINI_ARRAY] != NULL)
+    {
+      ElfW(Addr) *array =
+	(ElfW(Addr) *) (map->l_addr
+			+ map->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
+      unsigned int sz = (map->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
+			 / sizeof (ElfW(Addr)));
+
+      while (sz-- > 0)
+	((fini_t) array[sz]) ();
+    }
+
+  /* Next try the old-style destructor.  */
+  if (map->l_info[DT_FINI] != NULL)
+    DL_CALL_DT_FINI (map, ((void *) map->l_addr
+			   + map->l_info[DT_FINI]->d_un.d_ptr));
+}
 
 void
 _dl_close_worker (struct link_map *map, bool force)
@@ -267,7 +291,8 @@ _dl_close_worker (struct link_map *map, bool force)
 		  && (imap->l_flags_1 & DF_1_NODELETE) == 0);
 
 	  /* Call its termination function.  Do not do it for
-	     half-cooked objects.  */
+	     half-cooked objects.  Temporarily disable exception
+	     handling, so that errors are fatal.  */
 	  if (imap->l_init_called)
 	    {
 	      /* When debugging print a message first.  */
@@ -276,22 +301,9 @@ _dl_close_worker (struct link_map *map, bool force)
 		_dl_debug_printf ("\ncalling fini: %s [%lu]\n\n",
 				  imap->l_name, nsid);
 
-	      if (imap->l_info[DT_FINI_ARRAY] != NULL)
-		{
-		  ElfW(Addr) *array =
-		    (ElfW(Addr) *) (imap->l_addr
-				    + imap->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
-		  unsigned int sz = (imap->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
-				     / sizeof (ElfW(Addr)));
-
-		  while (sz-- > 0)
-		    ((fini_t) array[sz]) ();
-		}
-
-	      /* Next try the old-style destructor.  */
-	      if (imap->l_info[DT_FINI] != NULL)
-		DL_CALL_DT_FINI (imap, ((void *) imap->l_addr
-			 + imap->l_info[DT_FINI]->d_un.d_ptr));
+	      if (imap->l_info[DT_FINI_ARRAY] != NULL
+		  || imap->l_info[DT_FINI] != NULL)
+		_dl_catch_exception (NULL, call_destructors, imap);
 	    }
 
 #ifdef SHARED
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 8d699d3ff8..533fb96e8f 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -177,6 +177,23 @@ _dl_find_dso_for_object (const ElfW(Addr) addr)
 }
 rtld_hidden_def (_dl_find_dso_for_object);
 
+/* struct dl_init_args and call_dl_init are used to call _dl_init with
+   exception handling disabled.  */
+struct dl_init_args
+{
+  struct link_map *new;
+  int argc;
+  char **argv;
+  char **env;
+};
+
+static void
+call_dl_init (void *closure)
+{
+  struct dl_init_args *args = closure;
+  _dl_init (args->new, args->argc, args->argv, args->env);
+}
+
 static void
 dl_open_worker (void *a)
 {
@@ -509,8 +526,19 @@ TLS generation counter wrapped!  Please report this."));
   DL_STATIC_INIT (new);
 #endif
 
-  /* Run the initializer functions of new objects.  */
-  _dl_init (new, args->argc, args->argv, args->env);
+  /* Run the initializer functions of new objects.  Temporarily
+     disable the exception handler, so that lazy binding failures are
+     fatal.  */
+  {
+    struct dl_init_args init_args =
+      {
+        .new = new,
+        .argc = args->argc,
+        .argv = args->argv,
+        .env = args->env
+      };
+    _dl_catch_exception (NULL, call_dl_init, &init_args);
+  }
 
   /* Now we can make the new map available in the global scope.  */
   if (mode & RTLD_GLOBAL)
diff --git a/elf/tst-finilazyfailmod.c b/elf/tst-finilazyfailmod.c
new file mode 100644
index 0000000000..2670bd1a94
--- /dev/null
+++ b/elf/tst-finilazyfailmod.c
@@ -0,0 +1,27 @@
+/* Helper module for tst-initfinilazyfail: lazy binding failure in destructor.
+   Copyright (C) 2019 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/>.  */
+
+/* An undefined function.  Calling it will cause a lazy binding
+   failure.  */
+void undefined_function (void);
+
+static void __attribute__ ((destructor))
+fini (void)
+{
+  undefined_function ();
+}
diff --git a/elf/tst-initfinilazyfail.c b/elf/tst-initfinilazyfail.c
new file mode 100644
index 0000000000..9b4a3d0c0f
--- /dev/null
+++ b/elf/tst-initfinilazyfail.c
@@ -0,0 +1,84 @@
+/* Test that lazy binding failures in constructors and destructors are fatal.
+   Copyright (C) 2019 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 <dlfcn.h>
+#include <string.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/xdlfcn.h>
+
+static void
+test_constructor (void *closure)
+{
+  void *handle = dlopen ("tst-initlazyfailmod.so", RTLD_LAZY);
+  if (handle == NULL)
+    FAIL_EXIT (2, "dlopen did not terminate the process: %s", dlerror ());
+  else
+    FAIL_EXIT (2, "dlopen did not terminate the process (%p)", handle);
+}
+
+static void
+test_destructor (void *closure)
+{
+  void *handle = xdlopen ("tst-finilazyfailmod.so", RTLD_LAZY);
+  int ret = dlclose (handle);
+  const char *message = dlerror ();
+  if (message != NULL)
+    FAIL_EXIT (2, "dlclose did not terminate the process: %d, %s",
+               ret, message);
+  else
+    FAIL_EXIT (2, "dlopen did not terminate the process: %d", ret);
+}
+
+static int
+do_test (void)
+{
+  {
+    struct support_capture_subprocess proc
+      = support_capture_subprocess (test_constructor, NULL);
+    support_capture_subprocess_check (&proc, "constructor", 127,
+                                      sc_allow_stderr);
+    printf ("info: constructor failure output: [[%s]]\n", proc.err.buffer);
+    TEST_VERIFY (strstr (proc.err.buffer,
+                         "tst-initfinilazyfail: symbol lookup error: ")
+                 != NULL);
+    TEST_VERIFY (strstr (proc.err.buffer,
+                         "tst-initlazyfailmod.so: undefined symbol:"
+                         " undefined_function\n") != NULL);
+    support_capture_subprocess_free (&proc);
+  }
+
+  {
+    struct support_capture_subprocess proc
+      = support_capture_subprocess (test_destructor, NULL);
+    support_capture_subprocess_check (&proc, "destructor", 127,
+                                      sc_allow_stderr);
+    printf ("info: destructor failure output: [[%s]]\n", proc.err.buffer);
+    TEST_VERIFY (strstr (proc.err.buffer,
+                         "tst-initfinilazyfail: symbol lookup error: ")
+                 != NULL);
+    TEST_VERIFY (strstr (proc.err.buffer,
+                         "tst-finilazyfailmod.so: undefined symbol:"
+                         " undefined_function\n") != NULL);
+    support_capture_subprocess_free (&proc);
+  }
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/elf/tst-initlazyfailmod.c b/elf/tst-initlazyfailmod.c
new file mode 100644
index 0000000000..36348b58d6
--- /dev/null
+++ b/elf/tst-initlazyfailmod.c
@@ -0,0 +1,27 @@
+/* Helper module for tst-initfinilazyfail: lazy binding failure in constructor.
+   Copyright (C) 2019 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/>.  */
+
+/* An undefined function.  Calling it will cause a lazy binding
+   failure.  */
+void undefined_function (void);
+
+static void __attribute__ ((constructor))
+init (void)
+{
+  undefined_function ();
+}