[RFC,v8,16/20] Suppress inter-namespace DSO sharing for audit libraries

Message ID 20210209171839.7911-17-vivek@collabora.com
State Superseded
Delegated to: Adhemerval Zanella Netto
Headers
Series Implementation of RTLD_SHARED for dlmopen |

Commit Message

Vivek Dasmohapatra Feb. 9, 2021, 5:18 p.m. UTC
  Audit libraries should not participate in DSO sharing: In
particular libraries tagged with DF_GNU_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(-)
  

Comments

Adhemerval Zanella Netto Feb. 19, 2021, 8:39 p.m. UTC | #1
On 09/02/2021 14:18, Vivek Das Mohapatra via Libc-alpha wrote:
> Audit libraries should not participate in DSO sharing: In
> particular libraries tagged with DF_GNU_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 --git a/bits/dlfcn.h b/bits/dlfcn.h
> index 0daa789693..f910528b32 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_GNU_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

Ok, the idea is to export the new flag to userpace.

> diff --git a/elf/dl-load.c b/elf/dl-load.c
> index 4e8e7ca031..44fd3b5489 100644
> --- a/elf/dl-load.c
> +++ b/elf/dl-load.c
> @@ -1079,7 +1079,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
>    /* DSOs in the main namespace which are flagged DF_GNU_1_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)
> @@ -1174,7 +1174,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
>  
>    /* We need to check for DT_GNU_FLAGS_1/DF_GNU_1_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

Ok.

> @@ -2188,6 +2188,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 38b3587d4a..d3c3e32be2 100644
> --- a/elf/dl-open.c
> +++ b/elf/dl-open.c
> @@ -485,9 +485,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;

Use 'bool' here.

>    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;
> +

Indentation seems off here (4 space instead of 2).

>    /* 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
> @@ -539,6 +546,7 @@ dl_open_worker (void *a)
>       mode and set 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);
> @@ -650,7 +658,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.  */

Ok.

>    for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i)
> diff --git a/elf/rtld.c b/elf/rtld.c
> index 596b6ac3d9..82069658b3 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -659,7 +659,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 1331771a17..a0a35bba5d 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_GNU_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.  */
> 

Ok.
  

Patch

diff --git a/bits/dlfcn.h b/bits/dlfcn.h
index 0daa789693..f910528b32 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_GNU_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 4e8e7ca031..44fd3b5489 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1079,7 +1079,7 @@  _dl_map_object_from_fd (const char *name, const char *origname, int fd,
   /* DSOs in the main namespace which are flagged DF_GNU_1_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)
@@ -1174,7 +1174,7 @@  _dl_map_object_from_fd (const char *name, const char *origname, int fd,
 
   /* We need to check for DT_GNU_FLAGS_1/DF_GNU_1_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
@@ -2188,6 +2188,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 38b3587d4a..d3c3e32be2 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -485,9 +485,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
@@ -539,6 +546,7 @@  dl_open_worker (void *a)
      mode and set 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);
@@ -650,7 +658,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 596b6ac3d9..82069658b3 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -659,7 +659,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 1331771a17..a0a35bba5d 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_GNU_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.  */