diff mbox series

[RFC,v5,13/16] Suppress inter-namespace DSO sharing for audit libraries

Message ID 20200617140024.12777-14-vivek@collabora.com
State New
Headers show
Series Proof-of-Concept implementation of RTLD_SHARED for dlmopen | expand

Commit Message

Vivek Das Mohapatra June 17, 2020, 2 p.m. UTC
Audit libraries should not participate in DSO sharing: In
particular libraries tagged with DF_1_UNIQUE should not be
shared between the audit namespace and any others - they
should get their own copy.

This is signalled to the loader code by passing the RTLD_ISOLATE
flag from the relevant entry point in the dl modes argument.
---
 bits/dlfcn.h              |  3 +++
 elf/dl-load.c             |  5 +++--
 elf/dl-open.c             | 13 +++++++++++--
 elf/rtld.c                |  2 +-
 sysdeps/mips/bits/dlfcn.h |  3 +++
 5 files changed, 21 insertions(+), 5 deletions(-)
diff mbox series

Patch

diff --git a/bits/dlfcn.h b/bits/dlfcn.h
index 9170059323..ef079709f9 100644
--- a/bits/dlfcn.h
+++ b/bits/dlfcn.h
@@ -32,6 +32,9 @@ 
    visible as if the object were linked directly into the program.  */
 #define RTLD_GLOBAL	0x00100
 
+/* Suppress RTLD_SHARED and/or DF_1_UNIQUE.  */
+#define RTLD_ISOLATE 0x00040
+
 /* If the following bit is set in the MODE argument to dlmopen
    then the target object is loaded into the main namespace (if
    it is not already there) and a shallow copy (proxy) is placed
diff --git a/elf/dl-load.c b/elf/dl-load.c
index e0eced6dd2..bf86d3c7f9 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1000,7 +1000,7 @@  _dl_map_object_from_fd (const char *name, const char *origname, int fd,
   /* DSOs in the main namespace which are flagged DT_GNU_UNIQUE should only
      be opened into the main namespace. Other namespaces should only get
      proxies.  */
-  if (__glibc_unlikely (nsid != LM_ID_BASE))
+  if (__glibc_unlikely ((nsid != LM_ID_BASE) && !(mode & RTLD_ISOLATE)))
     {
       /* Check base ns to see if the name matched another already loaded.  */
       for (l = GL(dl_ns)[LM_ID_BASE]._ns_loaded; l != NULL; l = l->l_next)
@@ -1092,7 +1092,7 @@  _dl_map_object_from_fd (const char *name, const char *origname, int fd,
 
   /* We need to check for DT_GNU_UNIQUE before we start
      initialising any namespace dependent metatada.  */
-  if (nsid != LM_ID_BASE)
+  if (__glibc_unlikely ((nsid != LM_ID_BASE) && !(mode & RTLD_ISOLATE)))
     {
       /* Target DSO is flagged as unique: Make sure it gets loaded into
          the base namespace. It is up to our caller to generate a proxy in
@@ -2083,6 +2083,7 @@  _dl_map_object (struct link_map *loader, const char *name,
 
   assert (nsid >= 0);
   assert (nsid < GL(dl_nns));
+  assert (!((mode & RTLD_ISOLATE) && (mode & RTLD_SHARED)));
 
 #ifdef SHARED
   /* Only need to do proxy checks if `nsid' is not LM_ID_BASE.  */
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 43597a510e..67d5825510 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -477,9 +477,16 @@  dl_open_worker (void *a)
   int mode = args->mode;
   struct link_map *call_map = NULL;
   struct link_map *preloaded = NULL;
-  int want_proxy = mode & RTLD_SHARED;
+  int want_proxy = 0;
+  int dl_isolate = mode & RTLD_ISOLATE;
   Lmid_t proxy_ns = LM_ID_BASE;
 
+  /* Isolation means we should suppress all inter-namespace sharing.  */
+  if (dl_isolate)
+      mode &= ~RTLD_SHARED;
+  else
+      want_proxy = mode & RTLD_SHARED;
+
   /* Determine the caller's map if necessary.  This is needed in case
      we have a DST, when we don't know the namespace ID we have to put
      the new object in, or when the file name has no path in which
@@ -526,6 +533,7 @@  dl_open_worker (void *a)
      want_proxy.
      NOTE: RTLD_ISOLATE in the mode suppresses this behaviour.  */
   if (__glibc_unlikely (args->nsid != LM_ID_BASE) &&
+      __glibc_likely (!dl_isolate) &&
       __glibc_likely (!want_proxy))
     {
       preloaded = _dl_find_dso (file, LM_ID_BASE);
@@ -636,7 +644,8 @@  dl_open_worker (void *a)
 
   /* Load that object's dependencies.  */
   _dl_map_object_deps (new, NULL, 0, 0,
-		       mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT));
+		       mode & (__RTLD_DLOPEN | RTLD_DEEPBIND | __RTLD_AUDIT |
+		               RTLD_ISOLATE));
 
   /* So far, so good.  Now check the versions.  */
   for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i)
diff --git a/elf/rtld.c b/elf/rtld.c
index 553cfbd1b7..be08786419 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -616,7 +616,7 @@  dlmopen_doit (void *a)
   struct dlmopen_args *args = (struct dlmopen_args *) a;
   args->map = _dl_open (args->fname,
 			(RTLD_LAZY | __RTLD_DLOPEN | __RTLD_AUDIT
-			 | __RTLD_SECURE),
+			 | __RTLD_SECURE | RTLD_ISOLATE),
 			dl_main, LM_ID_NEWLM, _dl_argc, _dl_argv,
 			__environ);
 }
diff --git a/sysdeps/mips/bits/dlfcn.h b/sysdeps/mips/bits/dlfcn.h
index fc33fd184e..372dcf4097 100644
--- a/sysdeps/mips/bits/dlfcn.h
+++ b/sysdeps/mips/bits/dlfcn.h
@@ -39,6 +39,9 @@ 
    share a single instance of a DSO.  */
 #define RTLD_SHARED 0x00020
 
+/* Suppress RTLD_SHARED and/or DF_1_UNIQUE.  */
+#define RTLD_ISOLATE 0x00040
+
 /* Unix98 demands the following flag which is the inverse to RTLD_GLOBAL.
    The implementation does this by default and so we can define the
    value to zero.  */