]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
Initial implemenation of full dlmopen support. carlos/dlmopen
authorCarlos O'Donell <carlos@systemhalted.org>
Fri, 18 Sep 2015 04:12:22 +0000 (00:12 -0400)
committerCarlos O'Donell <carlos@systemhalted.org>
Fri, 18 Sep 2015 04:12:22 +0000 (00:12 -0400)
dlfcn/dlmopen.c
dlfcn/dlopen.c
elf/dl-open.c
elf/dl-support.c
elf/dl-sym.c
elf/tst-dlmopen1.c

index 38dca7abc8cc8b9e9f70809a53cc5a21e184b71b..ba468d21f0effe812f843053d5436fcd5b4fd9bd 100644 (file)
@@ -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,
index 8bf2752d9c6d2aa05a4d435d0dcf1e74b9c10ed6..c5e5f1e044e92bb6e0702c2e58919efd0698c3eb 100644 (file)
@@ -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);
 }
 
index 5429d181cbb61bbab7825795a86717509d49344e..6beac774b0266ae16f051f8531986d655f41c5ab 100644 (file)
@@ -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;
     }
index e7b5110b5935359961b37307886b90d91bdba16d..93670cf5b51e05b78106261d26de469b9b721e0f 100644 (file)
@@ -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);
index 56fea86fa88075e20359824b7de9ef17f43b3615..917b540e55e8ff16159bda58d98f82dd9600177d 100644 (file)
@@ -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)
     {
index 9839267d8f1d231c36a63ec501e77739b4ad0896..5a05891846545f5a5c260f59ace132dba2562951 100644 (file)
@@ -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;
     }