[RFC,v4,12/15] When loading new DSOs into alternate namespaces check for DF_1_UNIQUE

Message ID 930baed596c837b84f80cf18eef95426efba88cc.1585588166.git.vivek@collabora.com
State New
Headers show
Series
  • Proof-of-Concept implementation of RTLD_SHARED for dlmopen
Related show

Commit Message

Vineet Gupta via Libc-alpha March 30, 2020, 5:43 p.m.
If a DSO has not already been loaded and the target is not the main
namespace then we must check to see if it's been DF_1_UNIQUE tagged
and load it into the main namespace instead.

dl_open_worker has alread been modified to notice the discrepancy
between the request and the result in such cases, and will set up
a proxy in the target namespace.
---
 elf/dl-load.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 86 insertions(+), 14 deletions(-)

-- 
2.11.0

Patch

diff --git a/elf/dl-load.c b/elf/dl-load.c
index 62820167d5..a2d78f8615 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -827,6 +827,63 @@  _dl_init_paths (const char *llp)
     env_path_list.dirs = (void *) -1;
 }
 
+static ElfW(Word)
+_get_dt_flags (int fd, const ElfW(Ehdr) *header, const ElfW(Phdr) *phdr)
+{
+  ElfW(Word) flags = 0;
+  const ElfW(Phdr) *ph;
+  ElfW(Dyn) entry = {};
+  off_t reset;
+  off_t pos;
+  off_t end;
+
+  reset = __lseek (fd, 0, SEEK_CUR);
+
+  for (ph = phdr; ph < &phdr[header->e_phnum]; ++ph)
+    {
+      switch (ph->p_type)
+	{
+        case PT_DYNAMIC:
+          // dt_flags_1 = get_dt_flags_1( fd, ph->p_offset, ph->p_filesz );
+          pos = __lseek (fd, ph->p_offset, SEEK_SET);
+          end = pos + ph->p_filesz;
+
+          while (pos < end)
+            {
+              ssize_t rb = 0;
+              do
+                {
+                  ssize_t rretl = __read_nocancel (fd, &entry + rb,
+                                                   sizeof (ElfW(Dyn)) - rb);
+                  if (rretl <= 0)
+                    goto cleanup;
+
+                  rb += rretl;
+                }
+              while (__glibc_unlikely (rb < sizeof (ElfW(Dyn))));
+
+              switch (entry.d_tag)
+                {
+                case DT_FLAGS_1:
+                  flags = entry.d_un.d_val;
+                case DT_NULL:
+                  goto cleanup;
+                  break;
+                default:
+                  break;
+                }
+              pos += rb;
+            }
+          break;
+        }
+    }
+
+ cleanup:
+  /* Put the file descriptor offset back where it was when we were called.  */
+  __lseek (fd, reset, SEEK_SET);
+
+  return flags;
+}
 
 static void
 __attribute__ ((noreturn, noinline))
@@ -868,6 +925,7 @@  _dl_map_object_from_fd (const char *name, const char *origname, int fd,
   const ElfW(Ehdr) *header;
   const ElfW(Phdr) *phdr;
   const ElfW(Phdr) *ph;
+  ElfW(Word) dt_flags_1 = 0;
   size_t maplength;
   int type;
   /* Initialize to keep the compiler happy.  */
@@ -1019,6 +1077,34 @@  _dl_map_object_from_fd (const char *name, const char *origname, int fd,
   else
     assert (r->r_state == RT_ADD);
 
+  /* Load the ELF header using preallocated struct space if it's big enough.  */
+  maplength = header->e_phnum * sizeof (ElfW(Phdr));
+  if (header->e_phoff + maplength <= (size_t) fbp->len)
+    phdr = (void *) (fbp->buf + header->e_phoff);
+  else
+    {
+      phdr = alloca (maplength);
+      if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength,
+				       header->e_phoff) != maplength)
+	{
+	  errstring = N_("cannot read file data");
+	  goto call_lose_errno;
+	}
+    }
+
+  /* We need to check for DT_FLAGS_1 & DF_1_UNIQUE before we start
+     initialising any namespace dependent metatada.  */
+  if (nsid != LM_ID_BASE)
+    {
+      dt_flags_1 = _get_dt_flags (fd, header, phdr);
+
+      /* 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
+         the target nsid.  */
+      if (dt_flags_1 & DF_1_UNIQUE)
+        nsid = LM_ID_BASE;
+    }
+
   /* Enter the new object in the list of loaded objects.  */
   l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
   if (__glibc_unlikely (l == NULL))
@@ -1036,20 +1122,6 @@  _dl_map_object_from_fd (const char *name, const char *origname, int fd,
   type = header->e_type;
   l->l_phnum = header->e_phnum;
 
-  maplength = header->e_phnum * sizeof (ElfW(Phdr));
-  if (header->e_phoff + maplength <= (size_t) fbp->len)
-    phdr = (void *) (fbp->buf + header->e_phoff);
-  else
-    {
-      phdr = alloca (maplength);
-      if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength,
-				       header->e_phoff) != maplength)
-	{
-	  errstring = N_("cannot read file data");
-	  goto call_lose_errno;
-	}
-    }
-
    /* On most platforms presume that PT_GNU_STACK is absent and the stack is
     * executable.  Other platforms default to a nonexecutable stack and don't
     * need PT_GNU_STACK to do so.  */