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