Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add forced deletion support to _dl_close_worker
https://sourceware.org/bugzilla/show_bug.cgi?id=17833

I've a shared library that contains both undefined and unique symbols.
Then I try to call the following sequence of dlopen:

1. dlopen("./libfoo.so", RTLD_NOW)
2. dlopen("./libfoo.so", RTLD_LAZY | RTLD_GLOBAL)

First dlopen call terminates with error because of undefined symbols,
but STB_GNU_UNIQUE ones set DF_1_NODELETE flag and hence block library
in the memory.

The library goes into inconsistent state as several structures remain
uninitialized. For instance, relocations for GOT table were not performed.

By the time of second dlopen call this library looks like as it would be
fully initialized but this is not true: any call through incorrect GOT
table leads to segmentation fault.  On some systems this inconsistency
triggers assertions in the dynamic linker.

This patch adds a parameter to _dl_close_worker to implement forced object
deletion in case of dlopen() failure:

1. Clears DF_1_NODELETE bit if forced, to allow library to be removed from
memory.
2. For each unique symbol that is defined in this object clears
appropriate entry in _ns_unique_sym_table.

	[BZ #17833]
	* elf/Makefile (tests): Add tst-nodelete.
	(modules-names): Add tst-nodelete-uniquemod.
	(tst-nodelete-uniquemod.so-no-z-defs): New.
	(tst-nodelete-rtldmod.so-no-z-defs): Likewise.
	(tst-nodelete-zmod.so-no-z-defs): Likewise.
	($(objpfx)tst-nodelete): Likewise.
	($(objpfx)tst-nodelete.out): Likewise.
	(LDFLAGS-tst-nodelete): Likewise.
	(LDFLAGS-tst-nodelete-zmod.so): Likewise.
	* elf/dl-close.c (_dl_close_worker): Add a parameter to
	implement forced object deletion.
	(_dl_close): Pass false to _dl_close_worker.
	* elf/dl-open.c (_dl_open): Pass true to _dl_close_worker.
	* elf/tst-nodelete.cc: New file.
	* elf/tst-nodeletelib.cc: Likewise.
	* elf/tst-znodeletelib.cc: Likewise.
	* include/dlfcn.h (_dl_close_worker): Add a new parameter.
  • Loading branch information
Pavel Kopyl authored and H.J. Lu committed Jul 7, 2015
1 parent 890b7a4 commit 02d5e5d
Show file tree
Hide file tree
Showing 11 changed files with 167 additions and 19 deletions.
22 changes: 22 additions & 0 deletions ChangeLog
@@ -1,3 +1,25 @@
2015-07-07 Pavel Kopyl <p.kopyl@samsung.com>
Mikhail Ilin <m.ilin@samsung.com>

[BZ #17833]
* elf/Makefile (tests): Add tst-nodelete.
(modules-names): Add tst-nodelete-uniquemod.
(tst-nodelete-uniquemod.so-no-z-defs): New.
(tst-nodelete-rtldmod.so-no-z-defs): Likewise.
(tst-nodelete-zmod.so-no-z-defs): Likewise.
($(objpfx)tst-nodelete): Likewise.
($(objpfx)tst-nodelete.out): Likewise.
(LDFLAGS-tst-nodelete): Likewise.
(LDFLAGS-tst-nodelete-zmod.so): Likewise.
* elf/dl-close.c (_dl_close_worker): Add a parameter to
implement forced object deletion.
(_dl_close): Pass false to _dl_close_worker.
* elf/dl-open.c (_dl_open): Pass true to _dl_close_worker.
* elf/tst-nodelete.cc: New file.
* elf/tst-nodeletelib.cc: Likewise.
* elf/tst-znodeletelib.cc: Likewise.
* include/dlfcn.h (_dl_close_worker): Add a new parameter.

2015-07-07 Stefan Liebler <stli@linux.vnet.ibm.com>

[BZ #18508]
Expand Down
26 changes: 13 additions & 13 deletions NEWS
Expand Up @@ -14,19 +14,19 @@ Version 2.22
16526, 16538, 16559, 16560, 16704, 16783, 16850, 17053, 17090, 17195,
17269, 17293, 17322, 17403, 17523, 17542, 17569, 17581, 17588, 17596,
17620, 17621, 17628, 17631, 17692, 17711, 17715, 17776, 17779, 17792,
17836, 17841, 17912, 17916, 17930, 17932, 17944, 17949, 17964, 17965,
17967, 17969, 17977, 17978, 17987, 17991, 17996, 17998, 17999, 18007,
18019, 18020, 18029, 18030, 18032, 18034, 18036, 18038, 18039, 18042,
18043, 18046, 18047, 18049, 18068, 18080, 18093, 18100, 18104, 18110,
18111, 18116, 18125, 18128, 18138, 18185, 18196, 18197, 18206, 18210,
18211, 18217, 18219, 18220, 18221, 18234, 18244, 18245, 18247, 18287,
18319, 18324, 18333, 18346, 18371, 18397, 18409, 18410, 18412, 18418,
18422, 18434, 18435, 18444, 18468, 18469, 18470, 18479, 18483, 18495,
18496, 18497, 18498, 18502, 18507, 18508, 18512, 18513, 18519, 18520,
18522, 18527, 18528, 18529, 18530, 18532, 18533, 18534, 18536, 18539,
18540, 18542, 18544, 18545, 18546, 18547, 18549, 18553, 18558, 18569,
18583, 18585, 18586, 18592, 18593, 18594, 18602, 18612, 18613, 18619,
18633.
17833, 17836, 17841, 17912, 17916, 17930, 17932, 17944, 17949, 17964,
17965, 17967, 17969, 17977, 17978, 17987, 17991, 17996, 17998, 17999,
18007, 18019, 18020, 18029, 18030, 18032, 18034, 18036, 18038, 18039,
18042, 18043, 18046, 18047, 18049, 18068, 18080, 18093, 18100, 18104,
18110, 18111, 18116, 18125, 18128, 18138, 18185, 18196, 18197, 18206,
18210, 18211, 18217, 18219, 18220, 18221, 18234, 18244, 18245, 18247,
18287, 18319, 18324, 18333, 18346, 18371, 18397, 18409, 18410, 18412,
18418, 18422, 18434, 18435, 18444, 18468, 18469, 18470, 18479, 18483,
18495, 18496, 18497, 18498, 18502, 18507, 18508, 18512, 18513, 18519,
18520, 18522, 18527, 18528, 18529, 18530, 18532, 18533, 18534, 18536,
18539, 18540, 18542, 18544, 18545, 18546, 18547, 18549, 18553, 18558,
18569, 18583, 18585, 18586, 18592, 18593, 18594, 18602, 18612, 18613,
18619, 18633.

* Cache information can be queried via sysconf() function on s390 e.g. with
_SC_LEVEL1_ICACHE_SIZE as argument.
Expand Down
17 changes: 15 additions & 2 deletions elf/Makefile
Expand Up @@ -132,7 +132,7 @@ tests += $(tests-static)
ifeq (yes,$(build-shared))
tests += loadtest restest1 preloadtest loadfail multiload origtest resolvfail \
constload1 order noload filter unload \
reldep reldep2 reldep3 reldep4 nodelete nodelete2 \
reldep reldep2 reldep3 reldep4 nodelete nodelete2 tst-nodelete \
nodlopen nodlopen2 neededtest neededtest2 \
neededtest3 neededtest4 unload2 lateglobal initfirst global \
restest2 next dblload dblunload reldep5 reldep6 reldep7 reldep8 \
Expand Down Expand Up @@ -207,7 +207,9 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
tst-unique1mod1 tst-unique1mod2 \
tst-unique2mod1 tst-unique2mod2 \
tst-auditmod9a tst-auditmod9b \
$(if $(CXX),tst-unique3lib tst-unique3lib2 tst-unique4lib) \
$(if $(CXX),tst-unique3lib tst-unique3lib2 tst-unique4lib \
tst-nodelete-uniquemod) \
tst-nodelete-rtldmod tst-nodelete-zmod \
tst-initordera1 tst-initorderb1 \
tst-initordera2 tst-initorderb2 \
tst-initordera3 tst-initordera4 \
Expand Down Expand Up @@ -591,6 +593,9 @@ ifuncmod5.so-no-z-defs = yes
ifuncmod6.so-no-z-defs = yes
tst-auditmod9a.so-no-z-defs = yes
tst-auditmod9b.so-no-z-defs = yes
tst-nodelete-uniquemod.so-no-z-defs = yes
tst-nodelete-rtldmod.so-no-z-defs = yes
tst-nodelete-zmod.so-no-z-defs = yes

ifeq ($(build-shared),yes)
# Build all the modules even when not actually running test programs.
Expand Down Expand Up @@ -1153,6 +1158,14 @@ $(objpfx)tst-unique3.out: $(objpfx)tst-unique3lib2.so

$(objpfx)tst-unique4: $(objpfx)tst-unique4lib.so

$(objpfx)tst-nodelete: $(libdl)
$(objpfx)tst-nodelete.out: $(objpfx)tst-nodelete-uniquemod.so \
$(objpfx)tst-nodelete-rtldmod.so \
$(objpfx)tst-nodelete-zmod.so

LDFLAGS-tst-nodelete = -rdynamic
LDFLAGS-tst-nodelete-zmod.so = -Wl,--enable-new-dtags,-z,nodelete

$(objpfx)tst-initorder-cmp.out: tst-initorder.exp $(objpfx)tst-initorder.out
cmp $^ > $@; \
$(evaluate-test)
Expand Down
33 changes: 31 additions & 2 deletions elf/dl-close.c
Expand Up @@ -108,7 +108,7 @@ remove_slotinfo (size_t idx, struct dtv_slotinfo_list *listp, size_t disp,


void
_dl_close_worker (struct link_map *map)
_dl_close_worker (struct link_map *map, bool force)
{
/* One less direct use. */
--map->l_direct_opencount;
Expand Down Expand Up @@ -152,6 +152,10 @@ _dl_close_worker (struct link_map *map)
l->l_idx = idx;
maps[idx] = l;
++idx;

/* Clear DF_1_NODELETE to force object deletion. */
if (force)
l->l_flags_1 &= ~DF_1_NODELETE;
}
assert (idx == nloaded);

Expand Down Expand Up @@ -635,6 +639,31 @@ _dl_close_worker (struct link_map *map)
}
}

/* Reset unique symbols if forced. */
if (force)
{
struct unique_sym_table *tab = &ns->_ns_unique_sym_table;
__rtld_lock_lock_recursive (tab->lock);
struct unique_sym *entries = tab->entries;
if (entries != NULL)
{
size_t idx, size = tab->size;
for (idx = 0; idx < size; ++idx)
{
/* Clear unique symbol entries that belong to this
object. */
if (entries[idx].name != NULL
&& entries[idx].map == imap)
{
entries[idx].name = NULL;
entries[idx].hashval = 0;
tab->n_elements--;
}
}
}
__rtld_lock_unlock_recursive (tab->lock);
}

/* We can unmap all the maps at once. We determined the
start address and length when we loaded the object and
the `munmap' call does the rest. */
Expand Down Expand Up @@ -782,7 +811,7 @@ _dl_close (void *_map)
/* Acquire the lock. */
__rtld_lock_lock_recursive (GL(dl_load_lock));

_dl_close_worker (map);
_dl_close_worker (map, false);

__rtld_lock_unlock_recursive (GL(dl_load_lock));
}
2 changes: 1 addition & 1 deletion elf/dl-open.c
Expand Up @@ -670,7 +670,7 @@ no more namespaces available for dlmopen()"));
if ((mode & __RTLD_AUDIT) == 0)
GL(dl_tls_dtv_gaps) = true;

_dl_close_worker (args.map);
_dl_close_worker (args.map, true);
}

assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
Expand Down
6 changes: 6 additions & 0 deletions elf/tst-nodelete-rtldmod.cc
@@ -0,0 +1,6 @@
extern int not_exist (void);

int foo (void)
{
return not_exist ();
}
14 changes: 14 additions & 0 deletions elf/tst-nodelete-uniquemod.cc
@@ -0,0 +1,14 @@
extern int not_exist (void);

inline int make_unique (void)
{
/* Static variables in inline functions and classes
generate STB_GNU_UNIQUE symbols. */
static int unique;
return ++unique;
}

int foo (void)
{
return make_unique () + not_exist ();
}
6 changes: 6 additions & 0 deletions elf/tst-nodelete-zmod.cc
@@ -0,0 +1,6 @@
extern int not_exist (void);

int foo (void)
{
return not_exist ();
}
51 changes: 51 additions & 0 deletions elf/tst-nodelete.cc
@@ -0,0 +1,51 @@
#include "../dlfcn/dlfcn.h"
#include <stdio.h>
#include <stdlib.h>

static int
do_test (void)
{
int result = 0;

/* This is a test for correct handling of dlopen failures for library that
is loaded with RTLD_NODELETE flag. The first dlopen should fail because
of undefined symbols in shared library. The second dlopen then verifies
that library was properly unloaded. */
if (dlopen ("tst-nodelete-rtldmod.so", RTLD_NOW | RTLD_NODELETE) != NULL
|| dlopen ("tst-nodelete-rtldmod.so", RTLD_LAZY | RTLD_NOLOAD) != NULL)
{
printf ("RTLD_NODELETE test failed\n");
result = 1;
}

/* This is a test for correct handling of dlopen failures for library that
is linked with '-z nodelete' option and hence has DF_1_NODELETE flag.
The first dlopen should fail because of undefined symbols in shared
library. The second dlopen then verifies that library was properly
unloaded. */
if (dlopen ("tst-nodelete-zmod.so", RTLD_NOW) != NULL
|| dlopen ("tst-nodelete-zmod.so", RTLD_LAZY | RTLD_NOLOAD) != NULL)
{
printf ("-z nodelete test failed\n");
result = 1;
}

/* This is a test for correct handling of dlopen failures for library
with unique symbols. The first dlopen should fail because of undefined
symbols in shared library. The second dlopen then verifies that library
was properly unloaded. */
if (dlopen ("tst-nodelete-uniquemod.so", RTLD_NOW) != NULL
|| dlopen ("tst-nodelete-uniquemod.so", RTLD_LAZY | RTLD_NOLOAD) != NULL)
{
printf ("Unique symbols test failed\n");
result = 1;
}

if (result == 0)
printf ("SUCCESS\n");

return result;
}

#define TEST_FUNCTION do_test ()
#include "../test-skeleton.c"
6 changes: 6 additions & 0 deletions elf/tst-znodelete-zlib.cc
@@ -0,0 +1,6 @@
extern int not_exist (void);

int foo (void)
{
return not_exist ();
}
3 changes: 2 additions & 1 deletion include/dlfcn.h
Expand Up @@ -54,7 +54,8 @@ struct link_map;
extern void _dl_close (void *map) attribute_hidden;
/* Same as above, but without locking and safety checks for user
provided map arguments. */
extern void _dl_close_worker (struct link_map *map) attribute_hidden;
extern void _dl_close_worker (struct link_map *map, bool force)
attribute_hidden;

/* Look up NAME in shared object HANDLE (which may be RTLD_DEFAULT or
RTLD_NEXT). WHO is the calling function, for RTLD_NEXT. Returns
Expand Down

0 comments on commit 02d5e5d

Please sign in to comment.