diff options
-rw-r--r-- | NEWS | 6 | ||||
-rw-r--r-- | elf/Makefile | 15 | ||||
-rw-r--r-- | elf/dl-close.c | 46 | ||||
-rw-r--r-- | elf/dl-open.c | 32 | ||||
-rw-r--r-- | elf/tst-finilazyfailmod.c | 27 | ||||
-rw-r--r-- | elf/tst-initfinilazyfail.c | 84 | ||||
-rw-r--r-- | elf/tst-initlazyfailmod.c | 27 |
7 files changed, 216 insertions, 21 deletions
@@ -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 b05af5c..30c3896 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 c32e647..33486b9 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 8d699d3..533fb96 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 0000000..2670bd1 --- /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 0000000..9b4a3d0 --- /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 0000000..36348b5 --- /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 (); +} |