8a8cfb
commit 79e0cd7b3c997e211fad44a81fd839dc5b2546e8
8a8cfb
Author: Florian Weimer <fweimer@redhat.com>
8a8cfb
Date:   Wed Nov 27 16:20:47 2019 +0100
8a8cfb
8a8cfb
    Lazy binding failures during dlopen/dlclose must be fatal [BZ #24304]
8a8cfb
    
8a8cfb
    If a lazy binding failure happens during the execution of an ELF
8a8cfb
    constructor or destructor, the dynamic loader catches the error
8a8cfb
    and reports it using the dlerror mechanism.  This is undesirable
8a8cfb
    because there could be other constructors and destructors that
8a8cfb
    need processing (which are skipped), and the process is in an
8a8cfb
    inconsistent state at this point.  Therefore, we have to issue
8a8cfb
    a fatal dynamic loader error error and terminate the process.
8a8cfb
    
8a8cfb
    Note that the _dl_catch_exception in _dl_open is just an inner catch,
8a8cfb
    to roll back some state locally.  If called from dlopen, there is
8a8cfb
    still an outer catch, which is why calling _dl_init via call_dl_init
8a8cfb
    and a no-exception is required and cannot be avoiding by moving the
8a8cfb
    _dl_init call directly into _dl_open.
8a8cfb
    
8a8cfb
    _dl_fini does not need changes because it does not install an error
8a8cfb
    handler, so errors are already fatal there.
8a8cfb
    
8a8cfb
    Change-Id: I6b1addfe2e30f50a1781595f046f44173db9491a
8a8cfb
8a8cfb
Conflicts:
8a8cfb
	elf/Makefile
8a8cfb
	  (Usual conflicts due to test backport differences.)
8a8cfb
8a8cfb
diff --git a/elf/Makefile b/elf/Makefile
8a8cfb
index 74a240b3a68ff5e2..b752f6366400d221 100644
8a8cfb
--- a/elf/Makefile
8a8cfb
+++ b/elf/Makefile
8a8cfb
@@ -191,7 +191,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
8a8cfb
 	 tst-nodelete2 tst-audit11 tst-audit12 tst-dlsym-error tst-noload \
8a8cfb
 	 tst-latepthread tst-tls-manydynamic tst-nodelete-dlclose \
8a8cfb
 	 tst-debug1 tst-main1 tst-absolute-sym tst-absolute-zero tst-big-note \
8a8cfb
-	 tst-sonamemove-link tst-sonamemove-dlopen
8a8cfb
+	 tst-sonamemove-link tst-sonamemove-dlopen tst-initfinilazyfail
8a8cfb
 #	 reldep9
8a8cfb
 tests-internal += loadtest unload unload2 circleload1 \
8a8cfb
 	 neededtest neededtest2 neededtest3 neededtest4 \
8a8cfb
@@ -281,7 +281,8 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
8a8cfb
 		tst-main1mod tst-libc_dlvsym-dso tst-absolute-sym-lib \
8a8cfb
 		tst-absolute-zero-lib tst-big-note-lib \
8a8cfb
 		tst-sonamemove-linkmod1 \
8a8cfb
-		tst-sonamemove-runmod1 tst-sonamemove-runmod2
8a8cfb
+		tst-sonamemove-runmod1 tst-sonamemove-runmod2 \
8a8cfb
+		tst-initlazyfailmod tst-finilazyfailmod
8a8cfb
 
8a8cfb
 ifeq (yes,$(have-mtls-dialect-gnu2))
8a8cfb
 tests += tst-gnu2-tls1
8a8cfb
@@ -1526,3 +1527,13 @@ tst-libc_dlvsym-static-ENV = \
8a8cfb
 $(objpfx)tst-libc_dlvsym-static.out: $(objpfx)tst-libc_dlvsym-dso.so
8a8cfb
 
8a8cfb
 $(objpfx)tst-big-note: $(objpfx)tst-big-note-lib.so
8a8cfb
+
8a8cfb
+$(objpfx)tst-initfinilazyfail: $(libdl)
8a8cfb
+$(objpfx)tst-initfinilazyfail.out: \
8a8cfb
+  $(objpfx)tst-initlazyfailmod.so $(objpfx)tst-finilazyfailmod.so
8a8cfb
+# Override -z defs, so that we can reference an undefined symbol.
8a8cfb
+# Force lazy binding for the same reason.
8a8cfb
+LDFLAGS-tst-initlazyfailmod.so = \
8a8cfb
+  -Wl,-z,lazy -Wl,--unresolved-symbols=ignore-all
8a8cfb
+LDFLAGS-tst-finilazyfailmod.so = \
8a8cfb
+  -Wl,-z,lazy -Wl,--unresolved-symbols=ignore-all
8a8cfb
diff --git a/elf/dl-close.c b/elf/dl-close.c
8a8cfb
index ecd6729704ea3294..88aeea25839a34e0 100644
8a8cfb
--- a/elf/dl-close.c
8a8cfb
+++ b/elf/dl-close.c
8a8cfb
@@ -106,6 +106,30 @@ remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp,
8a8cfb
   return false;
8a8cfb
 }
8a8cfb
 
8a8cfb
+/* Invoke dstructors for CLOSURE (a struct link_map *).  Called with
8a8cfb
+   exception handling temporarily disabled, to make errors fatal.  */
8a8cfb
+static void
8a8cfb
+call_destructors (void *closure)
8a8cfb
+{
8a8cfb
+  struct link_map *map = closure;
8a8cfb
+
8a8cfb
+  if (map->l_info[DT_FINI_ARRAY] != NULL)
8a8cfb
+    {
8a8cfb
+      ElfW(Addr) *array =
8a8cfb
+	(ElfW(Addr) *) (map->l_addr
8a8cfb
+			+ map->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
8a8cfb
+      unsigned int sz = (map->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
8a8cfb
+			 / sizeof (ElfW(Addr)));
8a8cfb
+
8a8cfb
+      while (sz-- > 0)
8a8cfb
+	((fini_t) array[sz]) ();
8a8cfb
+    }
8a8cfb
+
8a8cfb
+  /* Next try the old-style destructor.  */
8a8cfb
+  if (map->l_info[DT_FINI] != NULL)
8a8cfb
+    DL_CALL_DT_FINI (map, ((void *) map->l_addr
8a8cfb
+			   + map->l_info[DT_FINI]->d_un.d_ptr));
8a8cfb
+}
8a8cfb
 
8a8cfb
 void
8a8cfb
 _dl_close_worker (struct link_map *map, bool force)
8a8cfb
@@ -267,7 +291,8 @@ _dl_close_worker (struct link_map *map, bool force)
8a8cfb
 		  && (imap->l_flags_1 & DF_1_NODELETE) == 0);
8a8cfb
 
8a8cfb
 	  /* Call its termination function.  Do not do it for
8a8cfb
-	     half-cooked objects.  */
8a8cfb
+	     half-cooked objects.  Temporarily disable exception
8a8cfb
+	     handling, so that errors are fatal.  */
8a8cfb
 	  if (imap->l_init_called)
8a8cfb
 	    {
8a8cfb
 	      /* When debugging print a message first.  */
8a8cfb
@@ -276,22 +301,9 @@ _dl_close_worker (struct link_map *map, bool force)
8a8cfb
 		_dl_debug_printf ("\ncalling fini: %s [%lu]\n\n",
8a8cfb
 				  imap->l_name, nsid);
8a8cfb
 
8a8cfb
-	      if (imap->l_info[DT_FINI_ARRAY] != NULL)
8a8cfb
-		{
8a8cfb
-		  ElfW(Addr) *array =
8a8cfb
-		    (ElfW(Addr) *) (imap->l_addr
8a8cfb
-				    + imap->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
8a8cfb
-		  unsigned int sz = (imap->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
8a8cfb
-				     / sizeof (ElfW(Addr)));
8a8cfb
-
8a8cfb
-		  while (sz-- > 0)
8a8cfb
-		    ((fini_t) array[sz]) ();
8a8cfb
-		}
8a8cfb
-
8a8cfb
-	      /* Next try the old-style destructor.  */
8a8cfb
-	      if (imap->l_info[DT_FINI] != NULL)
8a8cfb
-		DL_CALL_DT_FINI (imap, ((void *) imap->l_addr
8a8cfb
-			 + imap->l_info[DT_FINI]->d_un.d_ptr));
8a8cfb
+	      if (imap->l_info[DT_FINI_ARRAY] != NULL
8a8cfb
+		  || imap->l_info[DT_FINI] != NULL)
8a8cfb
+		_dl_catch_exception (NULL, call_destructors, imap);
8a8cfb
 	    }
8a8cfb
 
8a8cfb
 #ifdef SHARED
8a8cfb
diff --git a/elf/dl-open.c b/elf/dl-open.c
8a8cfb
index 518a6cad699ec6d0..c9c0254ee74c4f4b 100644
8a8cfb
--- a/elf/dl-open.c
8a8cfb
+++ b/elf/dl-open.c
8a8cfb
@@ -177,6 +177,23 @@ _dl_find_dso_for_object (const ElfW(Addr) addr)
8a8cfb
 }
8a8cfb
 rtld_hidden_def (_dl_find_dso_for_object);
8a8cfb
 
8a8cfb
+/* struct dl_init_args and call_dl_init are used to call _dl_init with
8a8cfb
+   exception handling disabled.  */
8a8cfb
+struct dl_init_args
8a8cfb
+{
8a8cfb
+  struct link_map *new;
8a8cfb
+  int argc;
8a8cfb
+  char **argv;
8a8cfb
+  char **env;
8a8cfb
+};
8a8cfb
+
8a8cfb
+static void
8a8cfb
+call_dl_init (void *closure)
8a8cfb
+{
8a8cfb
+  struct dl_init_args *args = closure;
8a8cfb
+  _dl_init (args->new, args->argc, args->argv, args->env);
8a8cfb
+}
8a8cfb
+
8a8cfb
 static void
8a8cfb
 dl_open_worker (void *a)
8a8cfb
 {
8a8cfb
@@ -506,8 +523,19 @@ TLS generation counter wrapped!  Please report this."));
8a8cfb
   DL_STATIC_INIT (new);
8a8cfb
 #endif
8a8cfb
 
8a8cfb
-  /* Run the initializer functions of new objects.  */
8a8cfb
-  _dl_init (new, args->argc, args->argv, args->env);
8a8cfb
+  /* Run the initializer functions of new objects.  Temporarily
8a8cfb
+     disable the exception handler, so that lazy binding failures are
8a8cfb
+     fatal.  */
8a8cfb
+  {
8a8cfb
+    struct dl_init_args init_args =
8a8cfb
+      {
8a8cfb
+        .new = new,
8a8cfb
+        .argc = args->argc,
8a8cfb
+        .argv = args->argv,
8a8cfb
+        .env = args->env
8a8cfb
+      };
8a8cfb
+    _dl_catch_exception (NULL, call_dl_init, &init_args);
8a8cfb
+  }
8a8cfb
 
8a8cfb
   /* Now we can make the new map available in the global scope.  */
8a8cfb
   if (mode & RTLD_GLOBAL)
8a8cfb
diff --git a/elf/tst-finilazyfailmod.c b/elf/tst-finilazyfailmod.c
8a8cfb
new file mode 100644
8a8cfb
index 0000000000000000..2670bd1a9400d0ef
8a8cfb
--- /dev/null
8a8cfb
+++ b/elf/tst-finilazyfailmod.c
8a8cfb
@@ -0,0 +1,27 @@
8a8cfb
+/* Helper module for tst-initfinilazyfail: lazy binding failure in destructor.
8a8cfb
+   Copyright (C) 2019 Free Software Foundation, Inc.
8a8cfb
+   This file is part of the GNU C Library.
8a8cfb
+
8a8cfb
+   The GNU C Library is free software; you can redistribute it and/or
8a8cfb
+   modify it under the terms of the GNU Lesser General Public
8a8cfb
+   License as published by the Free Software Foundation; either
8a8cfb
+   version 2.1 of the License, or (at your option) any later version.
8a8cfb
+
8a8cfb
+   The GNU C Library is distributed in the hope that it will be useful,
8a8cfb
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
8a8cfb
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
8a8cfb
+   Lesser General Public License for more details.
8a8cfb
+
8a8cfb
+   You should have received a copy of the GNU Lesser General Public
8a8cfb
+   License along with the GNU C Library; if not, see
8a8cfb
+   <https://www.gnu.org/licenses/>.  */
8a8cfb
+
8a8cfb
+/* An undefined function.  Calling it will cause a lazy binding
8a8cfb
+   failure.  */
8a8cfb
+void undefined_function (void);
8a8cfb
+
8a8cfb
+static void __attribute__ ((destructor))
8a8cfb
+fini (void)
8a8cfb
+{
8a8cfb
+  undefined_function ();
8a8cfb
+}
8a8cfb
diff --git a/elf/tst-initfinilazyfail.c b/elf/tst-initfinilazyfail.c
8a8cfb
new file mode 100644
8a8cfb
index 0000000000000000..9b4a3d0c0ffbb7c6
8a8cfb
--- /dev/null
8a8cfb
+++ b/elf/tst-initfinilazyfail.c
8a8cfb
@@ -0,0 +1,84 @@
8a8cfb
+/* Test that lazy binding failures in constructors and destructors are fatal.
8a8cfb
+   Copyright (C) 2019 Free Software Foundation, Inc.
8a8cfb
+   This file is part of the GNU C Library.
8a8cfb
+
8a8cfb
+   The GNU C Library is free software; you can redistribute it and/or
8a8cfb
+   modify it under the terms of the GNU Lesser General Public
8a8cfb
+   License as published by the Free Software Foundation; either
8a8cfb
+   version 2.1 of the License, or (at your option) any later version.
8a8cfb
+
8a8cfb
+   The GNU C Library is distributed in the hope that it will be useful,
8a8cfb
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
8a8cfb
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
8a8cfb
+   Lesser General Public License for more details.
8a8cfb
+
8a8cfb
+   You should have received a copy of the GNU Lesser General Public
8a8cfb
+   License along with the GNU C Library; if not, see
8a8cfb
+   <https://www.gnu.org/licenses/>.  */
8a8cfb
+
8a8cfb
+#include <dlfcn.h>
8a8cfb
+#include <string.h>
8a8cfb
+#include <support/capture_subprocess.h>
8a8cfb
+#include <support/check.h>
8a8cfb
+#include <support/xdlfcn.h>
8a8cfb
+
8a8cfb
+static void
8a8cfb
+test_constructor (void *closure)
8a8cfb
+{
8a8cfb
+  void *handle = dlopen ("tst-initlazyfailmod.so", RTLD_LAZY);
8a8cfb
+  if (handle == NULL)
8a8cfb
+    FAIL_EXIT (2, "dlopen did not terminate the process: %s", dlerror ());
8a8cfb
+  else
8a8cfb
+    FAIL_EXIT (2, "dlopen did not terminate the process (%p)", handle);
8a8cfb
+}
8a8cfb
+
8a8cfb
+static void
8a8cfb
+test_destructor (void *closure)
8a8cfb
+{
8a8cfb
+  void *handle = xdlopen ("tst-finilazyfailmod.so", RTLD_LAZY);
8a8cfb
+  int ret = dlclose (handle);
8a8cfb
+  const char *message = dlerror ();
8a8cfb
+  if (message != NULL)
8a8cfb
+    FAIL_EXIT (2, "dlclose did not terminate the process: %d, %s",
8a8cfb
+               ret, message);
8a8cfb
+  else
8a8cfb
+    FAIL_EXIT (2, "dlopen did not terminate the process: %d", ret);
8a8cfb
+}
8a8cfb
+
8a8cfb
+static int
8a8cfb
+do_test (void)
8a8cfb
+{
8a8cfb
+  {
8a8cfb
+    struct support_capture_subprocess proc
8a8cfb
+      = support_capture_subprocess (test_constructor, NULL);
8a8cfb
+    support_capture_subprocess_check (&proc, "constructor", 127,
8a8cfb
+                                      sc_allow_stderr);
8a8cfb
+    printf ("info: constructor failure output: [[%s]]\n", proc.err.buffer);
8a8cfb
+    TEST_VERIFY (strstr (proc.err.buffer,
8a8cfb
+                         "tst-initfinilazyfail: symbol lookup error: ")
8a8cfb
+                 != NULL);
8a8cfb
+    TEST_VERIFY (strstr (proc.err.buffer,
8a8cfb
+                         "tst-initlazyfailmod.so: undefined symbol:"
8a8cfb
+                         " undefined_function\n") != NULL);
8a8cfb
+    support_capture_subprocess_free (&proc;;
8a8cfb
+  }
8a8cfb
+
8a8cfb
+  {
8a8cfb
+    struct support_capture_subprocess proc
8a8cfb
+      = support_capture_subprocess (test_destructor, NULL);
8a8cfb
+    support_capture_subprocess_check (&proc, "destructor", 127,
8a8cfb
+                                      sc_allow_stderr);
8a8cfb
+    printf ("info: destructor failure output: [[%s]]\n", proc.err.buffer);
8a8cfb
+    TEST_VERIFY (strstr (proc.err.buffer,
8a8cfb
+                         "tst-initfinilazyfail: symbol lookup error: ")
8a8cfb
+                 != NULL);
8a8cfb
+    TEST_VERIFY (strstr (proc.err.buffer,
8a8cfb
+                         "tst-finilazyfailmod.so: undefined symbol:"
8a8cfb
+                         " undefined_function\n") != NULL);
8a8cfb
+    support_capture_subprocess_free (&proc;;
8a8cfb
+  }
8a8cfb
+
8a8cfb
+  return 0;
8a8cfb
+}
8a8cfb
+
8a8cfb
+#include <support/test-driver.c>
8a8cfb
diff --git a/elf/tst-initlazyfailmod.c b/elf/tst-initlazyfailmod.c
8a8cfb
new file mode 100644
8a8cfb
index 0000000000000000..36348b58d634d2bb
8a8cfb
--- /dev/null
8a8cfb
+++ b/elf/tst-initlazyfailmod.c
8a8cfb
@@ -0,0 +1,27 @@
8a8cfb
+/* Helper module for tst-initfinilazyfail: lazy binding failure in constructor.
8a8cfb
+   Copyright (C) 2019 Free Software Foundation, Inc.
8a8cfb
+   This file is part of the GNU C Library.
8a8cfb
+
8a8cfb
+   The GNU C Library is free software; you can redistribute it and/or
8a8cfb
+   modify it under the terms of the GNU Lesser General Public
8a8cfb
+   License as published by the Free Software Foundation; either
8a8cfb
+   version 2.1 of the License, or (at your option) any later version.
8a8cfb
+
8a8cfb
+   The GNU C Library is distributed in the hope that it will be useful,
8a8cfb
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
8a8cfb
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
8a8cfb
+   Lesser General Public License for more details.
8a8cfb
+
8a8cfb
+   You should have received a copy of the GNU Lesser General Public
8a8cfb
+   License along with the GNU C Library; if not, see
8a8cfb
+   <https://www.gnu.org/licenses/>.  */
8a8cfb
+
8a8cfb
+/* An undefined function.  Calling it will cause a lazy binding
8a8cfb
+   failure.  */
8a8cfb
+void undefined_function (void);
8a8cfb
+
8a8cfb
+static void __attribute__ ((constructor))
8a8cfb
+init (void)
8a8cfb
+{
8a8cfb
+  undefined_function ();
8a8cfb
+}