From f3f30c9d5cb98fb90fd6dd2e5362b7fe3ace3720 Mon Sep 17 00:00:00 2001 From: Carlos O'Donell Date: Fri, 18 Sep 2015 00:12:22 -0400 Subject: Initial implemenation of full dlmopen support. --- dlfcn/dlmopen.c | 5 ----- dlfcn/dlopen.c | 2 +- elf/dl-open.c | 62 +++++++++++++++++++++++++++++++++++++++++++++--------- elf/dl-support.c | 13 ++++++++++++ elf/dl-sym.c | 13 +++++++++--- elf/tst-dlmopen1.c | 7 +++--- 6 files changed, 80 insertions(+), 22 deletions(-) diff --git a/dlfcn/dlmopen.c b/dlfcn/dlmopen.c index 38dca7abc8..ba468d21f0 100644 --- a/dlfcn/dlmopen.c +++ b/dlfcn/dlmopen.c @@ -61,11 +61,6 @@ dlmopen_doit (void *a) if (args->file == NULL) # endif GLRO(dl_signal_error) (EINVAL, NULL, NULL, N_("invalid namespace")); - - /* It makes no sense to use RTLD_GLOBAL when loading a DSO into - a namespace other than the base namespace. */ - if (__glibc_unlikely (args->mode & RTLD_GLOBAL)) - GLRO(dl_signal_error) (EINVAL, NULL, NULL, N_("invalid mode")); } args->new = GLRO(dl_open) (args->file ?: "", args->mode | __RTLD_DLOPEN, diff --git a/dlfcn/dlopen.c b/dlfcn/dlopen.c index 8bf2752d9c..c5e5f1e044 100644 --- a/dlfcn/dlopen.c +++ b/dlfcn/dlopen.c @@ -65,7 +65,7 @@ dlopen_doit (void *a) args->new = GLRO(dl_open) (args->file ?: "", args->mode | __RTLD_DLOPEN, args->caller, - args->file == NULL ? LM_ID_BASE : NS, + NS, __dlfcn_argc, __dlfcn_argv, __environ); } diff --git a/elf/dl-open.c b/elf/dl-open.c index 5429d181cb..6beac774b0 100644 --- a/elf/dl-open.c +++ b/elf/dl-open.c @@ -72,6 +72,31 @@ add_to_global (struct link_map *new) if (new->l_searchlist.r_list[cnt]->l_global == 0) ++to_add; + struct link_namespaces *ns = &GL(dl_ns)[new->l_ns]; + + if (__glibc_unlikely (new->l_ns != LM_ID_BASE + && ns->_ns_main_searchlist == NULL)) + { + /* An initial object was loaded with dlmopen into a distinct namespace + that has no global searchlist (RTLD_GLOBAL) and RTLD_GLOBAL was used. + Or that object then dlopened another object into the global + searchlist. We find ourselves with no global searchlist initialized. + We have two choices, either we forbid this scenario and return an + error or treat the first RTLD_GLOBAL DSOs searchlist as the global + searchlist of the namespace. We do the latter since it's the most + sensible course of action since you may dlmopen other libraries which + have no idea they have been isolated. Thus RTLD_GLOBAL dlopen calls + within the new namespace are restricted to the new namespace and may + reference the symbols of the initial RTLD_GLOBAL dlmopen'd + libraries. */ + ns->_ns_main_searchlist = &new->l_searchlist; + /* Treat this list like it is read-only. A value of zero forces a copy + later if we need to extend this list. The list itself is already + being used as the primary scope for the first loaded RTLD_GLOBAL + object into the new namespace, thus we don't want to free it. */ + ns->_ns_global_scope_alloc = 0; + } + /* The symbols of the new objects and its dependencies are to be introduced into the global scope that will be used to resolve references from other dynamically-loaded objects. @@ -86,7 +111,6 @@ add_to_global (struct link_map *new) in an realloc() call. Therefore we allocate a completely new array the first time we have to add something to the locale scope. */ - struct link_namespaces *ns = &GL(dl_ns)[new->l_ns]; if (ns->_ns_global_scope_alloc == 0) { /* This is the first dynamic object given global scope. */ @@ -204,15 +228,33 @@ dl_open_worker (void *a) { const void *caller_dlopen = args->caller_dlopen; - /* We have to find out from which object the caller is calling. - By default we assume this is the main application. */ - call_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; - - struct link_map *l = _dl_find_dso_for_object ((ElfW(Addr)) caller_dlopen); - - if (l) - call_map = l; - + call_map = _dl_find_dso_for_object ((ElfW(Addr)) caller_dlopen); + + /* We support dlmopen (ns, NULL, ...), which should do the same thing + as dlopen (NULL, ...). It should provide access to the base loaded + object in the namespace. In the case of LM_ID_BASE it's "" i.e. the + executable, but in the event of !LM_ID_BASE we need to return the + base object in the namespace. The FILE must be adjusted if we are + !LM_ID_BASE to be the name for the base object loaded into the + namespace or _dl_map_object will fail. */ + if (call_map != NULL && call_map->l_ns != LM_ID_BASE && file[0] == '\0') + file = GL(dl_ns)[call_map->l_ns]._ns_loaded->l_name; + + /* If we know we are using LM_ID_BASE explicitly, then fall back to + the base loaded object in that namespace if we failed to find the + caller. */ + if (call_map == NULL && args->nsid == LM_ID_BASE) + call_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; + + /* If we don't know know which namespace we are using then a failure to + determine the namespace of the caller is now a hard error. In the + past we might have returned LM_ID_BASE, but that's invalid for dlmopen + since it would lead to leakage outside of the namespace. */ + if (call_map == NULL && args->nsid == __LM_ID_CALLER) + _dl_signal_error (EINVAL, file, NULL, N_("\ +unable to determine caller's namespace")); + + /* Use the caller's namespace if we were loaded into that namespace. */ if (args->nsid == __LM_ID_CALLER) args->nsid = call_map->l_ns; } diff --git a/elf/dl-support.c b/elf/dl-support.c index e7b5110b59..93670cf5b5 100644 --- a/elf/dl-support.c +++ b/elf/dl-support.c @@ -305,9 +305,22 @@ void internal_function _dl_non_dynamic_init (void) { + extern const ElfW(Ehdr) __ehdr_start + __attribute__ ((weak, visibility ("hidden"))); + extern const ElfW(Addr) __data_start; + _dl_main_map.l_origin = _dl_get_origin (); _dl_main_map.l_phdr = GL(dl_phdr); _dl_main_map.l_phnum = GL(dl_phnum); + /* Starting from binutils-2.23, the linker will define __ehdr_start + pointing at the ELF header, which is a good enough marker for the + start of main. The start of data (assumed to come after text) is + good enough for l_map_end. We want l_map_start and l_map_end set + to allow _dl_find_dso_for_object to be able to associate + _dl_main_map with a caller's addresses within main. */ + _dl_main_map.l_map_start = (ElfW(Addr)) &__ehdr_start; + _dl_main_map.l_text_end = (ElfW(Addr)) &__data_start; + _dl_main_map.l_map_end = _dl_main_map.l_text_end; if (HP_SMALL_TIMING_AVAIL) HP_TIMING_NOW (_dl_cpuclock_offset); diff --git a/elf/dl-sym.c b/elf/dl-sym.c index 56fea86fa8..917b540e55 100644 --- a/elf/dl-sym.c +++ b/elf/dl-sym.c @@ -91,10 +91,17 @@ do_sym (void *handle, const char *name, void *who, lookup_t result; ElfW(Addr) caller = (ElfW(Addr)) who; + struct link_map *match = NULL; struct link_map *l = _dl_find_dso_for_object (caller); - /* If the address is not recognized the call comes from the main - program (we hope). */ - struct link_map *match = l ? l : GL(dl_ns)[LM_ID_BASE]._ns_loaded; + + /* Failure to determinet the caller's namespace is a fatal + error since we need it for handling namespace isolation + correctly. Falling back to LM_ID_BASE will break isolation. */ + if (l == NULL) + GLRO(dl_signal_error) (EINVAL, NULL, NULL, N_("\ +unable to determine caller's namespace during symbol lookup")); + + match = l; if (handle == RTLD_DEFAULT) { diff --git a/elf/tst-dlmopen1.c b/elf/tst-dlmopen1.c index 9839267d8f..5a05891846 100644 --- a/elf/tst-dlmopen1.c +++ b/elf/tst-dlmopen1.c @@ -2,6 +2,7 @@ #include #include +#define TEST_SO "$ORIGIN/tst-dlmopen1mod.so" static int do_test (void) @@ -34,7 +35,7 @@ do_test (void) return 1; } - h = dlmopen (LM_ID_NEWLM, "$ORIGIN/tst-dlmopen1mod.so", RTLD_LAZY); + h = dlmopen (LM_ID_NEWLM, TEST_SO, RTLD_LAZY); if (h == NULL) { printf ("cannot get handle for %s: %s\n", @@ -52,7 +53,7 @@ do_test (void) if (ns == LM_ID_BASE) { - printf ("namespace for %s is LM_ID_BASE\n", LIBC_SO); + printf ("namespace for %s is LM_ID_BASE\n", TEST_SO); return 1; } @@ -69,7 +70,7 @@ do_test (void) if (dlclose (h) != 0) { printf ("dlclose for %s in %s failed: %s\n", - LIBC_SO, __func__, dlerror ()); + TEST_SO, __func__, dlerror ()); return 1; } -- cgit 1.4.1