Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12-SP4:Update
glibc-utils.26366
dlclose-assert-init-called.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File dlclose-assert-init-called.patch of Package glibc-utils.26366
2016-12-24 Carlos O'Donell <carlos@redhat.com> [BZ #11941] * elf/dl-close.c (_dl_close): Take dl_load_lock to examine map. Remove assert (map->l_init_called); if DF_1_NODELETE is set. * elf/Makefile [ifeq (yes,$(build-shared))] (tests): Add tst-nodelete-dlclose. (modules-names): Add tst-nodelete-dlclose-dso and tst-nodelete-dlclose-plugin. ($(objpfx)tst-nodelete-dlclose-dso.so): Define. ($(objpfx)tst-nodelete-dlclose-plugin.so): Define. ($(objpfx)tst-nodelete-dlclose): Define. ($(objpfx)tst-nodelete-dlclose.out): Define. Index: glibc-2.22/elf/Makefile =================================================================== --- glibc-2.22.orig/elf/Makefile +++ glibc-2.22/elf/Makefile @@ -148,7 +148,8 @@ tests += loadtest restest1 preloadtest l tst-unique1 tst-unique2 $(if $(CXX),tst-unique3 tst-unique4 \ tst-nodelete) \ tst-initorder tst-initorder2 tst-relsort1 tst-null-argv \ - tst-ptrguard1 tst-tlsalign tst-tlsalign-extern tst-nodelete-opened + tst-ptrguard1 tst-tlsalign tst-tlsalign-extern tst-nodelete-opened \ + tst-nodelete-dlclose # reldep9 ifeq ($(build-hardcoded-path-in-tests),yes) tests += tst-dlopen-aout @@ -218,7 +219,8 @@ modules-names = testobj1 testobj2 testob tst-initorder2d \ tst-relsort1mod1 tst-relsort1mod2 tst-array2dep \ tst-array5dep tst-null-argv-lib \ - tst-tlsalign-lib tst-nodelete-opened-lib + tst-tlsalign-lib tst-nodelete-opened-lib \ + tst-nodelete-dlclose-dso tst-nodelete-dlclose-plugin ifeq (yes,$(have-protected-data)) modules-names += tst-protected1moda tst-protected1modb tests += tst-protected1a tst-protected1b @@ -1222,3 +1224,12 @@ $(objpfx)tst-prelink-cmp.out: tst-prelin $(objpfx)tst-prelink-conflict.out cmp $^ > $@; \ $(evaluate-test) + +# The application depends on the DSO, and the DSO loads the plugin. +# The plugin also depends on the DSO. This creates the circular +# dependency via dlopen that we're testing to make sure works. +$(objpfx)tst-nodelete-dlclose-dso.so: $(libdl) +$(objpfx)tst-nodelete-dlclose-plugin.so: $(objpfx)tst-nodelete-dlclose-dso.so +$(objpfx)tst-nodelete-dlclose: $(objpfx)tst-nodelete-dlclose-dso.so +$(objpfx)tst-nodelete-dlclose.out: $(objpfx)tst-nodelete-dlclose-dso.so \ + $(objpfx)tst-nodelete-dlclose-plugin.so Index: glibc-2.22/elf/dl-close.c =================================================================== --- glibc-2.22.orig/elf/dl-close.c +++ glibc-2.22/elf/dl-close.c @@ -804,19 +804,37 @@ _dl_close (void *_map) { struct link_map *map = _map; - /* First see whether we can remove the object at all. */ + /* We must take the lock to examine the contents of map and avoid + concurrent dlopens. */ + __rtld_lock_lock_recursive (GL(dl_load_lock)); + + /* At this point we are guaranteed nobody else is touching the list of + loaded maps, but a concurrent dlclose might have freed our map + before we took the lock. There is no way to detect this (see below) + so we proceed assuming this isn't the case. First see whether we + can remove the object at all. */ if (__glibc_unlikely (map->l_flags_1 & DF_1_NODELETE)) { - assert (map->l_init_called); /* Nope. Do nothing. */ + __rtld_lock_unlock_recursive (GL(dl_load_lock)); return; } + /* At present this is an unreliable check except in the case where the + caller has recursively called dlclose and we are sure the link map + has not been freed. In a non-recursive dlclose the map itself + might have been freed and this access is potentially a data race + with whatever other use this memory might have now, or worse we + might silently corrupt memory if it looks enough like a link map. + POSIX has language in dlclose that appears to guarantee that this + should be a detectable case and given that dlclose should be threadsafe + we need this to be a reliable detection. + This is bug 20990. */ if (__builtin_expect (map->l_direct_opencount, 1) == 0) - GLRO(dl_signal_error) (0, map->l_name, NULL, N_("shared object not open")); - - /* Acquire the lock. */ - __rtld_lock_lock_recursive (GL(dl_load_lock)); + { + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + _dl_signal_error (0, map->l_name, NULL, N_("shared object not open")); + } _dl_close_worker (map, false); Index: glibc-2.22/elf/tst-nodelete-dlclose-dso.c =================================================================== --- /dev/null +++ glibc-2.22/elf/tst-nodelete-dlclose-dso.c @@ -0,0 +1,90 @@ +/* Bug 11941: Improper assert map->l_init_called in dlclose. + Copyright (C) 2016 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 + <http://www.gnu.org/licenses/>. */ + +/* This is the primary DSO that is loaded by the appliation. This DSO + then loads a plugin with RTLD_NODELETE. This plugin depends on this + DSO. This dependency chain means that at application shutdown the + plugin will be destructed first. Thus by the time this DSO is + destructed we will be calling dlclose on an object that has already + been destructed. It is allowed to call dlclose in this way and + should not assert. */ +#include <stdio.h> +#include <stdlib.h> +#include <dlfcn.h> + +/* Plugin to load. */ +static void *plugin_lib = NULL; +/* Plugin function. */ +static void (*plugin_func) (void); +#define LIB_PLUGIN "tst-nodelete-dlclose-plugin.so" + +/* This function is never called but the plugin references it. + We do this to avoid any future --as-needed from removing the + plugin's DT_NEEDED on this DSO (required for the test). */ +void +primary_reference (void) +{ + printf ("INFO: Called primary_reference function.\n"); +} + +void +primary (void) +{ + char *error; + + plugin_lib = dlopen (LIB_PLUGIN, RTLD_NOW | RTLD_LOCAL | RTLD_NODELETE); + if (plugin_lib == NULL) + { + printf ("ERROR: Unable to load plugin library.\n"); + exit (EXIT_FAILURE); + } + dlerror (); + + plugin_func = (void (*) (void)) dlsym (plugin_lib, "plugin_func"); + error = dlerror (); + if (error != NULL) + { + printf ("ERROR: Unable to find symbol with error \"%s\".", + error); + exit (EXIT_FAILURE); + } + + return; +} + +__attribute__ ((destructor)) +static void +primary_dtor (void) +{ + int ret; + + printf ("INFO: Calling primary destructor.\n"); + + /* The destructor runs in the test driver also, which + hasn't called primary, in that case do nothing. */ + if (plugin_lib == NULL) + return; + + ret = dlclose (plugin_lib); + if (ret != 0) + { + printf ("ERROR: Calling dlclose failed with \"%s\"\n", + dlerror ()); + exit (EXIT_FAILURE); + } +} Index: glibc-2.22/elf/tst-nodelete-dlclose-plugin.c =================================================================== --- /dev/null +++ glibc-2.22/elf/tst-nodelete-dlclose-plugin.c @@ -0,0 +1,40 @@ +/* Bug 11941: Improper assert map->l_init_called in dlclose. + Copyright (C) 2016 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 + <http://www.gnu.org/licenses/>. */ + +/* This DSO simulates a plugin with a dependency on the + primary DSO loaded by the appliation. */ +#include <stdio.h> + +extern void primary_reference (void); + +void +plugin_func (void) +{ + printf ("INFO: Calling plugin function.\n"); + /* Need a reference to the DSO to ensure that a potential --as-needed + doesn't remove the DT_NEEDED entry which we rely upon to ensure + destruction ordering. */ + primary_reference (); +} + +__attribute__ ((destructor)) +static void +plugin_dtor (void) +{ + printf ("INFO: Calling plugin destructor.\n"); +} Index: glibc-2.22/elf/tst-nodelete-dlclose.c =================================================================== --- /dev/null +++ glibc-2.22/elf/tst-nodelete-dlclose.c @@ -0,0 +1,36 @@ +/* Bug 11941: Improper assert map->l_init_called in dlclose. + Copyright (C) 2016 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 + <http://www.gnu.org/licenses/>. */ + +/* This simulates an application using the primary DSO which loads the + plugin DSO. */ +#include <stdio.h> +#include <stdlib.h> + +extern void primary (void); + +static int +do_test (void) +{ + printf ("INFO: Starting application.\n"); + primary (); + printf ("INFO: Exiting application.\n"); + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c"
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor