about summary refs log tree commit diff
diff options
context:
space:
mode:
authorCarlos O'Donell <carlos@systemhalted.org>2015-09-18 00:12:22 -0400
committerCarlos O'Donell <carlos@systemhalted.org>2015-09-18 00:12:22 -0400
commitf3f30c9d5cb98fb90fd6dd2e5362b7fe3ace3720 (patch)
tree06b957bbe6f2b85471697b8341733b162fae9da2
parent8a44513e9ccb271e368b4aa3c9493b94a45e039d (diff)
downloadglibc-carlos/dlmopen.tar.gz
glibc-carlos/dlmopen.tar.xz
glibc-carlos/dlmopen.zip
Initial implemenation of full dlmopen support. carlos/dlmopen
-rw-r--r--dlfcn/dlmopen.c5
-rw-r--r--dlfcn/dlopen.c2
-rw-r--r--elf/dl-open.c62
-rw-r--r--elf/dl-support.c13
-rw-r--r--elf/dl-sym.c13
-rw-r--r--elf/tst-dlmopen1.c7
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 <stdio.h>
 #include <gnu/lib-names.h>
 
+#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;
     }