diff options
| author | Florian Weimer <fweimer@redhat.com> | 2019-12-13 10:18:24 +0100 |
|---|---|---|
| committer | Florian Weimer <fweimer@redhat.com> | 2019-12-13 10:18:24 +0100 |
| commit | 365624e2d2a342cdb693b4cc35d2312169959e28 (patch) | |
| tree | 4a17435022fd7b0c03690c7ad3444b0d3c030ced | |
| parent | 186e119bbd4a10895429ffe405ae96dc5c5634b8 (diff) | |
| download | glibc-365624e2d2a342cdb693b4cc35d2312169959e28.tar.xz glibc-365624e2d2a342cdb693b4cc35d2312169959e28.zip | |
dlopen: Fix issues related to NODELETE handling and relocations
The assumption behind the assert in activate_nodelete was wrong:
Inconsistency detected by ld.so: dl-open.c: 459: activate_nodelete:
Assertion `!imap->l_init_called || imap->l_type != lt_loaded' failed! (edit)
It can happen that an already-loaded object that is in the local
scope is promoted to NODELETE status, via binding to a unique
symbol.
Similarly, it is possible that such NODELETE promotion occurs to
an already-loaded object from the global scope. This is why the
loop in activate_nodelete has to cover all objects in the namespace
of the new object.
In do_lookup_unique, it could happen that the NODELETE status of
an already-loaded object was overwritten with a pending NODELETE
status. As a result, if dlopen fails, this could cause a loss of
the NODELETE status of the affected object, eventually resulting
in an incorrect unload.
Fixes commit f63b73814f74032c0e5d0a83300e3d864ef905e5 ("Remove all
loaded objects if dlopen fails, ignoring NODELETE [BZ #20839]").
23 files changed, 992 insertions, 32 deletions
diff --git a/elf/Makefile b/elf/Makefile index b2b3be203f..72a5aa88b1 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -191,7 +191,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \ tst-audit1 tst-audit2 tst-audit8 tst-audit9 \ tst-addr1 tst-thrlock \ tst-unique1 tst-unique2 $(if $(CXX),tst-unique3 tst-unique4 \ - tst-nodelete) \ + tst-nodelete tst-dlopen-nodelete-reloc) \ tst-initorder tst-initorder2 tst-relsort1 tst-null-argv \ tst-tlsalign tst-tlsalign-extern tst-nodelete-opened \ tst-nodelete2 tst-audit11 tst-audit12 tst-dlsym-error tst-noload \ @@ -271,7 +271,24 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ tst-auditmod9a tst-auditmod9b \ $(if $(CXX),tst-unique3lib tst-unique3lib2 tst-unique4lib \ tst-nodelete-uniquemod tst-nodelete-rtldmod \ - tst-nodelete-zmod) \ + tst-nodelete-zmod \ + tst-dlopen-nodelete-reloc-mod1 \ + tst-dlopen-nodelete-reloc-mod2 \ + tst-dlopen-nodelete-reloc-mod3 \ + tst-dlopen-nodelete-reloc-mod4 \ + tst-dlopen-nodelete-reloc-mod5 \ + tst-dlopen-nodelete-reloc-mod6 \ + tst-dlopen-nodelete-reloc-mod7 \ + tst-dlopen-nodelete-reloc-mod8 \ + tst-dlopen-nodelete-reloc-mod9 \ + tst-dlopen-nodelete-reloc-mod10 \ + tst-dlopen-nodelete-reloc-mod11 \ + tst-dlopen-nodelete-reloc-mod12 \ + tst-dlopen-nodelete-reloc-mod13 \ + tst-dlopen-nodelete-reloc-mod14 \ + tst-dlopen-nodelete-reloc-mod15 \ + tst-dlopen-nodelete-reloc-mod16 \ + tst-dlopen-nodelete-reloc-mod17) \ tst-initordera1 tst-initorderb1 \ tst-initordera2 tst-initorderb2 \ tst-initordera3 tst-initordera4 \ @@ -1627,3 +1644,48 @@ $(objpfx)tst-dlopenfailmod1.so: \ $(shared-thread-library) $(objpfx)tst-dlopenfaillinkmod.so LDFLAGS-tst-dlopenfaillinkmod.so = -Wl,-soname,tst-dlopenfail-missingmod.so $(objpfx)tst-dlopenfailmod2.so: $(shared-thread-library) + +$(objpfx)tst-dlopen-nodelete-reloc: $(libdl) +$(objpfx)tst-dlopen-nodelete-reloc.out: \ + $(objpfx)tst-dlopen-nodelete-reloc-mod1.so \ + $(objpfx)tst-dlopen-nodelete-reloc-mod2.so \ + $(objpfx)tst-dlopen-nodelete-reloc-mod3.so \ + $(objpfx)tst-dlopen-nodelete-reloc-mod4.so \ + $(objpfx)tst-dlopen-nodelete-reloc-mod5.so \ + $(objpfx)tst-dlopen-nodelete-reloc-mod6.so \ + $(objpfx)tst-dlopen-nodelete-reloc-mod7.so \ + $(objpfx)tst-dlopen-nodelete-reloc-mod8.so \ + $(objpfx)tst-dlopen-nodelete-reloc-mod9.so \ + $(objpfx)tst-dlopen-nodelete-reloc-mod10.so \ + $(objpfx)tst-dlopen-nodelete-reloc-mod11.so \ + $(objpfx)tst-dlopen-nodelete-reloc-mod12.so \ + $(objpfx)tst-dlopen-nodelete-reloc-mod13.so \ + $(objpfx)tst-dlopen-nodelete-reloc-mod14.so \ + $(objpfx)tst-dlopen-nodelete-reloc-mod15.so \ + $(objpfx)tst-dlopen-nodelete-reloc-mod16.so \ + $(objpfx)tst-dlopen-nodelete-reloc-mod17.so +tst-dlopen-nodelete-reloc-mod2.so-no-z-defs = yes +LDFLAGS-tst-dlopen-nodelete-reloc-mod2.so = -Wl,-z,nodelete +$(objpfx)tst-dlopen-nodelete-reloc-mod4.so: \ + $(objpfx)tst-dlopen-nodelete-reloc-mod3.so +LDFLAGS-tst-dlopen-nodelete-reloc-mod4.so = -Wl,--no-as-needed +$(objpfx)tst-dlopen-nodelete-reloc-mod5.so: \ + $(objpfx)tst-dlopen-nodelete-reloc-mod4.so +LDFLAGS-tst-dlopen-nodelete-reloc-mod5.so = -Wl,-z,nodelete,--no-as-needed +tst-dlopen-nodelete-reloc-mod5.so-no-z-defs = yes +tst-dlopen-nodelete-reloc-mod7.so-no-z-defs = yes +$(objpfx)tst-dlopen-nodelete-reloc-mod8.so: $(libdl) +$(objpfx)tst-dlopen-nodelete-reloc-mod10.so: $(libdl) +tst-dlopen-nodelete-reloc-mod11.so-no-z-defs = yes +$(objpfx)tst-dlopen-nodelete-reloc-mod13.so: \ + $(objpfx)tst-dlopen-nodelete-reloc-mod12.so +$(objpfx)tst-dlopen-nodelete-reloc-mod15.so: \ + $(objpfx)tst-dlopen-nodelete-reloc-mod14.so +tst-dlopen-nodelete-reloc-mod16.so-no-z-defs = yes +$(objpfx)tst-dlopen-nodelete-reloc-mod16.so: \ + $(objpfx)tst-dlopen-nodelete-reloc-mod15.so +LDFLAGS-tst-dlopen-nodelete-reloc-mod16.so = -Wl,--no-as-needed +$(objpfx)tst-dlopen-nodelete-reloc-mod17.so: \ + $(objpfx)tst-dlopen-nodelete-reloc-mod15.so \ + $(objpfx)tst-dlopen-nodelete-reloc-mod16.so +LDFLAGS-tst-dlopen-nodelete-reloc-mod17.so = -Wl,--no-as-needed diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c index a2e85a5568..99846918c3 100644 --- a/elf/dl-lookup.c +++ b/elf/dl-lookup.c @@ -311,12 +311,12 @@ do_lookup_unique (const char *undef_name, uint_fast32_t new_hash, enter_unique_sym (entries, size, new_hash, strtab + sym->st_name, sym, map); - if (map->l_type == lt_loaded) + if (map->l_type == lt_loaded + && map->l_nodelete == link_map_nodelete_inactive) { /* Make sure we don't unload this object by setting the appropriate flag. */ - if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS) - && map->l_nodelete == link_map_nodelete_inactive) + if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS)) _dl_debug_printf ("\ marking %s [%lu] as NODELETE due to unique symbol\n", map->l_name, map->l_ns); diff --git a/elf/dl-open.c b/elf/dl-open.c index df9f29a5e5..56f213323c 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -433,34 +433,21 @@ TLS generation counter wrapped! Please report this.")); after dlopen failure is not possible, so that _dl_close can clean up objects if necessary. */ static void -activate_nodelete (struct link_map *new, int mode) +activate_nodelete (struct link_map *new) { - if (mode & RTLD_NODELETE || new->l_nodelete == link_map_nodelete_pending) - { - if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES)) - _dl_debug_printf ("activating NODELETE for %s [%lu]\n", - new->l_name, new->l_ns); - new->l_nodelete = link_map_nodelete_active; - } - - for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i) - { - struct link_map *imap = new->l_searchlist.r_list[i]; - if (imap->l_nodelete == link_map_nodelete_pending) - { - if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES)) - _dl_debug_printf ("activating NODELETE for %s [%lu]\n", - imap->l_name, imap->l_ns); - - /* Only new objects should have set - link_map_nodelete_pending. Existing objects should not - have gained any new dependencies and therefore cannot - reach NODELETE status. */ - assert (!imap->l_init_called || imap->l_type != lt_loaded); + /* It is necessary to traverse the entire namespace. References to + objects in the global scope and unique symbol bindings can force + NODELETE status for objects outside the local scope. */ + for (struct link_map *l = GL (dl_ns)[new->l_ns]._ns_loaded; l != NULL; + l = l->l_next) + if (l->l_nodelete == link_map_nodelete_pending) + { + if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES)) + _dl_debug_printf ("activating NODELETE for %s [%lu]\n", + l->l_name, l->l_ns); - imap->l_nodelete = link_map_nodelete_active; - } - } + l->l_nodelete = link_map_nodelete_active; + } } /* struct dl_init_args and call_dl_init are used to call _dl_init with @@ -721,7 +708,7 @@ dl_open_worker (void *a) All memory allocations for new objects must have happened before. */ - activate_nodelete (new, mode); + activate_nodelete (new); /* Second stage after resize_scopes: Actually perform the scope update. After this, dlsym and lazy binding can bind to new diff --git a/elf/tst-dlopen-nodelete-reloc-mod1.c b/elf/tst-dlopen-nodelete-reloc-mod1.c new file mode 100644 index 0000000000..397d60a2d5 --- /dev/null +++ b/elf/tst-dlopen-nodelete-reloc-mod1.c @@ -0,0 +1,39 @@ +/* Test propagation of NODELETE to an already-loaded object via relocation. + Non-NODELETE helper module. + 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 <stdbool.h> +#include <stdio.h> +#include <unistd.h> + +/* Globally exported. Set by the main program to true before + termination, and used by tst-dlopen-nodelete-reloc-mod2.so to + trigger marking this module as NODELETE (and also for its destructor + check). */ +bool may_finalize_mod1 = false; + +static void __attribute__ ((destructor)) +fini (void) +{ + if (!may_finalize_mod1) + { + puts ("error: tst-dlopen-nodelete-reloc-mod1.so destructor" + " called too early"); + _exit (1); + } +} diff --git a/elf/tst-dlopen-nodelete-reloc-mod10.c b/elf/tst-dlopen-nodelete-reloc-mod10.c new file mode 100644 index 0000000000..30748b73ec --- /dev/null +++ b/elf/tst-dlopen-nodelete-reloc-mod10.c @@ -0,0 +1,41 @@ +/* Helper module to load tst-dlopen-nodelete-reloc-mod11.so. + 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 <stddef.h> +#include <stdio.h> +#include <unistd.h> + +static void *handle; + +static void __attribute__ ((constructor)) +init (void) +{ + handle = dlopen ("tst-dlopen-nodelete-reloc-mod11.so", RTLD_NOW); + if (handle == NULL) + { + printf ("error: dlopen in module 10: %s\n", dlerror ()); + _exit (1); + } +} + +static void __attribute__ ((destructor)) +fini (void) +{ + dlclose (handle); +} diff --git a/elf/tst-dlopen-nodelete-reloc-mod11.cc b/elf/tst-dlopen-nodelete-reloc-mod11.cc new file mode 100644 index 0000000000..48c910403e --- /dev/null +++ b/elf/tst-dlopen-nodelete-reloc-mod11.cc @@ -0,0 +1,49 @@ +/* Second module defining a unique symbol (loaded indirectly). + 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 "tst-dlopen-nodelete-reloc.h" + +#include <stdbool.h> +#include <stdio.h> +#include <unistd.h> + +/* Just a flag here, not used for NODELETE processing. */ +bool may_finalize_mod11 = false; + +/* Trigger the creation of a unique symbol reference. This should + cause tst-dlopen-nodelete-reloc-mod9.so to be marked as + NODELETE. */ + +extern template struct unique_symbol<9>; + +int +global_function_mod11 (void) +{ + return unique_symbol<9>::value; +} + +static void __attribute__ ((destructor)) +fini (void) +{ + if (!may_finalize_mod11) + { + puts ("error: tst-dlopen-nodelete-reloc-mod11.so destructor" + " called too early"); + _exit (1); + } +} diff --git a/elf/tst-dlopen-nodelete-reloc-mod12.cc b/elf/tst-dlopen-nodelete-reloc-mod12.cc new file mode 100644 index 0000000000..5c093fd02d --- /dev/null +++ b/elf/tst-dlopen-nodelete-reloc-mod12.cc @@ -0,0 +1,42 @@ +/* First module for NODELETE test defining a unique symbol (with DT_NEEDED). + 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 "tst-dlopen-nodelete-reloc.h" + +#include <stdbool.h> +#include <stdio.h> +#include <unistd.h> + +/* Just a flag here, not used for NODELETE processing. */ +bool may_finalize_mod12 = false; + +/* Explicit instantiation. This produces a unique symbol definition + which is not referenced by the library itself, so the library is + not marked NODELETE. */ +template struct unique_symbol<12>; + +static void __attribute__ ((destructor)) +fini (void) +{ + if (!may_finalize_mod12) + { + puts ("error: tst-dlopen-nodelete-reloc-mod12.so destructor" + " called too early"); + _exit (1); + } +} diff --git a/elf/tst-dlopen-nodelete-reloc-mod13.cc b/elf/tst-dlopen-nodelete-reloc-mod13.cc new file mode 100644 index 0000000000..caf4fd1cc9 --- /dev/null +++ b/elf/tst-dlopen-nodelete-reloc-mod13.cc @@ -0,0 +1,48 @@ +/* Second module for NODELETE test defining a unique symbol (with DT_NEEDED). + 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 "tst-dlopen-nodelete-reloc.h" + +#include <stdbool.h> +#include <stdio.h> +#include <unistd.h> + +/* Just a flag here, not used for NODELETE processing. */ +bool may_finalize_mod13 = false; + +extern template struct unique_symbol<12>; + +/* Trigger the creation of a unique symbol reference. This should + cause tst-dlopen-nodelete-reloc-mod12.so to be marked as + NODELETE. */ +int +global_function_mod13 (void) +{ + return unique_symbol<12>::value; +} + +static void __attribute__ ((destructor)) +fini (void) +{ + if (!may_finalize_mod13) + { + puts ("error: tst-dlopen-nodelete-reloc-mod13.so destructor" + " called too early"); + _exit (1); + } +} diff --git a/elf/tst-dlopen-nodelete-reloc-mod13.h b/elf/tst-dlopen-nodelete-reloc-mod13.h new file mode 100644 index 0000000000..5d338481a3 --- /dev/null +++ b/elf/tst-dlopen-nodelete-reloc-mod13.h @@ -0,0 +1,24 @@ +/* Inline function which produces a unique symbol. + 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/>. */ + +inline char * +third_function_with_local_static (void) +{ + static char local; + return &local; +} diff --git a/elf/tst-dlopen-nodelete-reloc-mod14.cc b/elf/tst-dlopen-nodelete-reloc-mod14.cc new file mode 100644 index 0000000000..e67621a2a2 --- /dev/null +++ b/elf/tst-dlopen-nodelete-reloc-mod14.cc @@ -0,0 +1,42 @@ +/* This object must retain NODELETE status after a dlopen failure. + 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 |
