From 27f1b2c66c67e601dd619a1def70a8fd7ca5eeba Mon Sep 17 00:00:00 2001 From: Leonid Lisovskiy Date: Wed, 13 Jan 2016 21:00:05 +0300 Subject: libdl: dlopen() mustn't forget RTLD_NODELETE flag If RTLD_NODELETE is passed to dlopen() rather than set on shared library itself, flag propagation to rtld_flags is missed. Test-case taken from glibc. Signed-off-by: Leonid Lisovskiy Signed-off-by: Waldemar Brodkorb --- ldso/libdl/libdl.c | 6 +- test/dlopen/Makefile.in | 9 ++- test/dlopen/nodelete.c | 205 ++++++++++++++++++++++++++++++++++++++++++++++++ test/dlopen/nodelmod1.c | 10 +++ test/dlopen/nodelmod2.c | 10 +++ test/dlopen/nodelmod3.c | 8 ++ test/dlopen/nodelmod4.c | 10 +++ 7 files changed, 254 insertions(+), 4 deletions(-) create mode 100644 test/dlopen/nodelete.c create mode 100644 test/dlopen/nodelmod1.c create mode 100644 test/dlopen/nodelmod2.c create mode 100644 test/dlopen/nodelmod3.c create mode 100644 test/dlopen/nodelmod4.c diff --git a/ldso/libdl/libdl.c b/ldso/libdl/libdl.c index 2c4b06411..08619bc37 100644 --- a/ldso/libdl/libdl.c +++ b/ldso/libdl/libdl.c @@ -394,7 +394,7 @@ static void *do_dlopen(const char *libname, int flag, ElfW(Addr) from) dyn_chain = (struct dyn_elf *) malloc(sizeof(struct dyn_elf)); memset(dyn_chain, 0, sizeof(struct dyn_elf)); dyn_chain->dyn = tpnt; - tpnt->rtld_flags |= (flag & RTLD_GLOBAL); + tpnt->rtld_flags |= (flag & (RTLD_GLOBAL|RTLD_NODELETE)); dyn_chain->next_handle = _dl_handles; _dl_handles = dyn_ptr = dyn_chain; @@ -407,7 +407,7 @@ static void *do_dlopen(const char *libname, int flag, ElfW(Addr) from) dyn_chain->init_fini.init_fini = handle->init_fini.init_fini; dyn_chain->init_fini.nlist = handle->init_fini.nlist; for (i = 0; i < dyn_chain->init_fini.nlist; i++) - dyn_chain->init_fini.init_fini[i]->rtld_flags |= (flag & RTLD_GLOBAL); + dyn_chain->init_fini.init_fini[i]->rtld_flags |= (flag & (RTLD_GLOBAL|RTLD_NODELETE)); dyn_chain->next = handle->next; break; } @@ -439,7 +439,7 @@ static void *do_dlopen(const char *libname, int flag, ElfW(Addr) from) if (!tpnt1) goto oops; - tpnt1->rtld_flags |= (flag & RTLD_GLOBAL); + tpnt1->rtld_flags |= (flag & (RTLD_GLOBAL|RTLD_NODELETE)); /* This list is for dlsym() and relocation */ dyn_ptr->next = (struct dyn_elf *) malloc(sizeof(struct dyn_elf)); diff --git a/test/dlopen/Makefile.in b/test/dlopen/Makefile.in index 85996def4..a02b2b96e 100644 --- a/test/dlopen/Makefile.in +++ b/test/dlopen/Makefile.in @@ -5,7 +5,7 @@ export UCLIBC_ONLY := 1 TESTS := dltest dltest2 dlstatic test1 test2 test3 dlundef dlafk dladdr \ - testscope + testscope nodelete ifneq ($(HAVE_SHARED),y) TESTS_DISABLED := test3 @@ -63,3 +63,10 @@ LDFLAGS_libtest3.so := -lpthread -Wl,-rpath,. LDFLAGS_libC.so := -ldl LDFLAGS_libB.so := ./libC.so -Wl,-rpath,. LDFLAGS_libA.so := ./libB.so -Wl,-rpath,. + +nodelete: nodelmod1.so nodelmod2.so nodelmod3.so +nodelmod3.so: nodelmod4.so +LDFLAGS_nodelete := -rdynamic -ldl +LDFLAGS_nodelmod1.so := -Wl,-z,nodelete +LDFLAGS_nodelmod3.so := ./nodelmod4.so +LDFLAGS_nodelmod4.so := -Wl,-z,nodelete diff --git a/test/dlopen/nodelete.c b/test/dlopen/nodelete.c new file mode 100644 index 000000000..07ff96136 --- /dev/null +++ b/test/dlopen/nodelete.c @@ -0,0 +1,205 @@ +#include +#include +#include +#include +#include + + +static sigjmp_buf jmpbuf; + + +int fini_ran; + + +static void +__attribute__ ((noreturn)) +handler (int sig) +{ + siglongjmp (jmpbuf, 1); +} + + +#define TEST_FUNCTION do_test () +static int +do_test (void) +{ + /* We are testing the two possibilities to mark an object as not deletable: + - marked on the linker commandline with `-z nodelete' + - with the RTLD_NODELETE flag at dlopen()-time. + + The test we are performing should be safe. We are loading the objects, + get the address of variables in the respective object, unload the object + and then try to read the variable. If the object is unloaded this + should lead to an segmentation fault. */ + void *p; + struct sigaction sa; + + sa.sa_handler = handler; + sigfillset (&sa.sa_mask); + sa.sa_flags = SA_RESTART; + + if (sigaction (SIGSEGV, &sa, NULL) == -1) + puts ("cannot install signal handler: %m"); + + p = dlopen ("nodelmod1.so", RTLD_LAZY); + if (p == NULL) + { + printf ("failed to load \"nodelmod1.so\": %s\n", dlerror ()); + exit (1); + } + else + { + int *varp; + + varp = dlsym (p, "var1"); + if (varp == NULL) + { + puts ("failed to get address of \"var1\" in \"nodelmod1.so\""); + exit (1); + } + else + { + *varp = 20000720; + + /* Now close the object. */ + fini_ran = 0; + if (dlclose (p) != 0) + { + puts ("failed to close \"nodelmod1.so\""); + exit (1); + } + else if (! sigsetjmp (jmpbuf, 1)) + { + /* Access the variable again. */ + if (*varp != 20000720) + { + puts ("\"var1\" value not correct"); + exit (1); + } + else if (fini_ran != 0) + { + puts ("destructor of \"nodelmod1.so\" ran"); + exit (1); + } + else + puts ("-z nodelete test succeeded"); + } + else + { + /* We caught an segmentation fault. */ + puts ("\"nodelmod1.so\" got deleted!"); + exit (1); + } + } + } + + p = dlopen ("nodelmod2.so", RTLD_LAZY | RTLD_NODELETE); + if (p == NULL) + { + printf ("failed to load \"nodelmod2.so\": %s\n", dlerror ()); + exit (1); + } + else + { + int *varp; + + varp = dlsym (p, "var2"); + if (varp == NULL) + { + puts ("failed to get address of \"var2\" in \"nodelmod2.so\""); + exit (1); + } + else + { + *varp = 42; + + /* Now close the object. */ + fini_ran = 0; + if (dlclose (p) != 0) + { + puts ("failed to close \"nodelmod2.so\""); + exit (1); + } + else if (! sigsetjmp (jmpbuf, 1)) + { + /* Access the variable again. */ + if (*varp != 42) + { + puts ("\"var2\" value not correct"); + exit (1); + } + else if (fini_ran != 0) + { + puts ("destructor of \"nodelmod2.so\" ran"); + exit (1); + } + else + puts ("RTLD_NODELETE test succeeded"); + } + else + { + /* We caught an segmentation fault. */ + puts ("\"nodelmod2.so\" got deleted!"); + exit (1); + } + } + } + + p = dlopen ("nodelmod3.so", RTLD_LAZY); + if (p == NULL) + { + printf ("failed to load \"nodelmod3.so\": %s\n", dlerror ()); + exit (1); + } + else + { + int *(*fctp) (void); + + fctp = dlsym (p, "addr"); + if (fctp == NULL) + { + puts ("failed to get address of \"addr\" in \"nodelmod3.so\""); + exit (1); + } + else + { + int *varp = fctp (); + + *varp = -1; + + /* Now close the object. */ + fini_ran = 0; + if (dlclose (p) != 0) + { + puts ("failed to close \"nodelmod3.so\""); + exit (1); + } + else if (! sigsetjmp (jmpbuf, 1)) + { + /* Access the variable again. */ + if (*varp != -1) + { + puts ("\"var_in_mod4\" value not correct"); + exit (1); + } + else if (fini_ran != 0) + { + puts ("destructor of \"nodelmod4.so\" ran"); + exit (1); + } + else + puts ("-z nodelete in dependency succeeded"); + } + else + { + /* We caught an segmentation fault. */ + puts ("\"nodelmod4.so\" got deleted!"); + exit (1); + } + } + } + + return 0; +} + +#include "../test-skeleton.c" diff --git a/test/dlopen/nodelmod1.c b/test/dlopen/nodelmod1.c new file mode 100644 index 000000000..51be080af --- /dev/null +++ b/test/dlopen/nodelmod1.c @@ -0,0 +1,10 @@ +extern int fini_ran; + +int var1 = 42; + +static void +__attribute__ ((__destructor__)) +destr (void) +{ + fini_ran = 1; +} diff --git a/test/dlopen/nodelmod2.c b/test/dlopen/nodelmod2.c new file mode 100644 index 000000000..ff2ffc2fc --- /dev/null +++ b/test/dlopen/nodelmod2.c @@ -0,0 +1,10 @@ +extern int fini_ran; + +int var2 = 100; + +static void +__attribute__ ((__destructor__)) +destr (void) +{ + fini_ran = 1; +} diff --git a/test/dlopen/nodelmod3.c b/test/dlopen/nodelmod3.c new file mode 100644 index 000000000..817c94db6 --- /dev/null +++ b/test/dlopen/nodelmod3.c @@ -0,0 +1,8 @@ +extern int var_in_mod4; +extern int *addr (void); + +int * +addr (void) +{ + return &var_in_mod4; +} diff --git a/test/dlopen/nodelmod4.c b/test/dlopen/nodelmod4.c new file mode 100644 index 000000000..d7fa89396 --- /dev/null +++ b/test/dlopen/nodelmod4.c @@ -0,0 +1,10 @@ +extern int fini_ran; + +int var_in_mod4 = 99; + +static void +__attribute__ ((__destructor__)) +destr (void) +{ + fini_ran = 1; +} -- cgit v1.2.3