From b632bdd3f7c0ba0c79f23f5e4404eebb87ebdaa8 Mon Sep 17 00:00:00 2001 From: Siddhesh Poyarekar Date: Thu, 16 Jul 2015 08:53:28 +0530 Subject: [PATCH] Set NODELETE flag when opening already open objects with RTLD_NODELETE The DF_1_NODELETE flag is set too late when opening a DSO, due to which, if a DSO is already open, subsequently opening it with RTLD_NODELETE fails to set the DF_1_NODELETE flag. This patch fixes this by setting the flag immediately after bumping the opencount. Verified on x86_64. [BZ #18676] * elf/tst-nodelete-opened.c: New test case. * elf/tst-nodelete-opened-lib.c: New test case module. * elf/Makefile (tests, modules-names): Use them. * elf/dl-open.c (dl_open_worker): Set DF_1_NODELETE flag early. --- ChangeLog | 9 +++++ NEWS | 2 +- elf/Makefile | 6 ++-- elf/dl-open.c | 11 +++--- elf/tst-nodelete-opened-lib.c | 19 ++++++++++ elf/tst-nodelete-opened.c | 67 +++++++++++++++++++++++++++++++++++ 6 files changed, 106 insertions(+), 8 deletions(-) create mode 100644 elf/tst-nodelete-opened-lib.c create mode 100644 elf/tst-nodelete-opened.c diff --git a/ChangeLog b/ChangeLog index b0eba065fd..7dd5e9a96d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2015-07-16 Siddhesh Poyarekar + + [BZ #18676] + * elf/tst-nodelete-opened.c: New test case. + * elf/tst-nodelete-opened-lib.c: New test case module. + * elf/Makefile (tests, modules-names): Use them. + * elf/dl-open.c (dl_open_worker): Set DF_1_NODELETE flag + early. + 2015-07-15 David S. Miller * sysdeps/sparc/nptl/pthread_barrier_init.c: Include diff --git a/NEWS b/NEWS index f91edc7804..fc8456051b 100644 --- a/NEWS +++ b/NEWS @@ -27,7 +27,7 @@ Version 2.22 18522, 18527, 18528, 18529, 18530, 18532, 18533, 18534, 18536, 18539, 18540, 18542, 18544, 18545, 18546, 18547, 18549, 18553, 18557, 18558, 18569, 18583, 18585, 18586, 18592, 18593, 18594, 18602, 18612, 18613, - 18619, 18633, 18641, 18643, 18648. + 18619, 18633, 18641, 18643, 18648, 18676. * Cache information can be queried via sysconf() function on s390 e.g. with _SC_LEVEL1_ICACHE_SIZE as argument. diff --git a/elf/Makefile b/elf/Makefile index 90d50588e1..4ceeaf89be 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -148,7 +148,7 @@ tests += loadtest restest1 preloadtest loadfail multiload origtest resolvfail \ 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-ptrguard1 tst-tlsalign tst-tlsalign-extern tst-nodelete-opened # reldep9 ifeq ($(build-hardcoded-path-in-tests),yes) tests += tst-dlopen-aout @@ -218,7 +218,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ tst-initorder2d \ tst-relsort1mod1 tst-relsort1mod2 tst-array2dep \ tst-array5dep tst-null-argv-lib \ - tst-tlsalign-lib + tst-tlsalign-lib tst-nodelete-opened-lib ifeq (yes,$(have-protected-data)) modules-names += tst-protected1moda tst-protected1modb tests += tst-protected1a tst-protected1b @@ -525,6 +525,8 @@ $(objpfx)tst-initordera4.so: $(objpfx)tst-initordera3.so $(objpfx)tst-initorder: $(objpfx)tst-initordera4.so $(objpfx)tst-initordera1.so $(objpfx)tst-initorderb2.so $(objpfx)tst-null-argv: $(objpfx)tst-null-argv-lib.so $(objpfx)tst-tlsalign: $(objpfx)tst-tlsalign-lib.so +$(objpfx)tst-nodelete-opened.out: $(objpfx)tst-nodelete-opened-lib.so +$(objpfx)tst-nodelete-opened: $(libdl) $(objpfx)tst-tlsalign-extern: $(objpfx)tst-tlsalign-vars.o $(objpfx)tst-tlsalign-extern-static: $(objpfx)tst-tlsalign-vars.o diff --git a/elf/dl-open.c b/elf/dl-open.c index 027c1e0eda..2db1c02148 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -226,6 +226,12 @@ dl_open_worker (void *a) args->map = new = _dl_map_object (call_map, file, lt_loaded, 0, mode | __RTLD_CALLMAP, args->nsid); + /* Mark the object as not deletable if the RTLD_NODELETE flags was passed. + Do this early so that we don't skip marking the object if it was + already loaded. */ + if (__glibc_unlikely (mode & RTLD_NODELETE)) + new->l_flags_1 |= DF_1_NODELETE; + /* If the pointer returned is NULL this means the RTLD_NOLOAD flag is set and the object is not already loaded. */ if (new == NULL) @@ -564,11 +570,6 @@ TLS generation counter wrapped! Please report this.")); /* It failed. */ return; - /* Mark the object as not deletable if the RTLD_NODELETE flags was - passed. */ - if (__glibc_unlikely (mode & RTLD_NODELETE)) - new->l_flags_1 |= DF_1_NODELETE; - #ifndef SHARED /* We must be the static _dl_open in libc.a. A static program that has loaded a dynamic object now has competition. */ diff --git a/elf/tst-nodelete-opened-lib.c b/elf/tst-nodelete-opened-lib.c new file mode 100644 index 0000000000..994702a9c5 --- /dev/null +++ b/elf/tst-nodelete-opened-lib.c @@ -0,0 +1,19 @@ +/* Verify that objects opened with RTLD_NODELETE are not unloaded - the DSO. + Copyright (C) 2015 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 + . */ + +int foo_var = 42; diff --git a/elf/tst-nodelete-opened.c b/elf/tst-nodelete-opened.c new file mode 100644 index 0000000000..43b00fa16d --- /dev/null +++ b/elf/tst-nodelete-opened.c @@ -0,0 +1,67 @@ +/* Verify that an already opened DSO opened agained with RTLD_NODELETE actually + sets the NODELETE flag. + + Copyright (C) 2015 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 + . */ + +#include +#include + +int +do_test (void) +{ + void *h1 = dlopen ("$ORIGIN/tst-nodelete-opened-lib.so", RTLD_LAZY); + if (h1 == NULL) + { + printf ("h1: failed to open DSO: %s\n", dlerror ()); + return 1; + } + + void *h2 = dlopen ("$ORIGIN/tst-nodelete-opened-lib.so", + RTLD_LAZY | RTLD_NODELETE); + if (h2 == NULL) + { + printf ("h2: failed to open DSO: %s\n", dlerror ()); + return 1; + } + + int *foo = dlsym (h2, "foo_var"); + if (foo == NULL) + { + printf ("failed to load symbol foo_var: %s\n", dlerror ()); + return 1; + } + + if (dlclose (h1) != 0) + { + printf ("h1: dlclose failed: %s\n", dlerror ()); + return 1; + } + + if (dlclose (h2) != 0) + { + printf ("h2: dlclose failed: %s\n", dlerror ()); + return 1; + } + + printf ("foo == %d\n", *foo); + + return 0; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c"