]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - elf/dl-load.c
Fix BZ 20419. A PT_NOTE in a binary could be arbitratily large, so using
[thirdparty/glibc.git] / elf / dl-load.c
index 2964464158501026c0f332d0b2014395479f011b..431236920f9d1b04fa98923195ca45f775262949 100644 (file)
@@ -1,5 +1,5 @@
 /* Map in a shared object's segments from the file.
-   Copyright (C) 1995-2017 Free Software Foundation, Inc.
+   Copyright (C) 1995-2018 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -33,7 +33,6 @@
 #include "dynamic-link.h"
 #include <abi-tag.h>
 #include <stackinfo.h>
-#include <caller.h>
 #include <sysdep.h>
 #include <stap-probe.h>
 #include <libc-pointer-arith.h>
@@ -180,8 +179,7 @@ is_trusted_path_normalize (const char *path, size_t len)
 
 
 static size_t
-is_dst (const char *start, const char *name, const char *str,
-       int is_path, int secure)
+is_dst (const char *start, const char *name, const char *str, int secure)
 {
   size_t len;
   bool is_curly = false;
@@ -206,14 +204,12 @@ is_dst (const char *start, const char *name, const char *str,
       /* Skip over closing curly brace and adjust for the --name.  */
       len += 2;
     }
-  else if (name[len] != '\0' && name[len] != '/'
-          && (!is_path || name[len] != ':'))
+  else if (name[len] != '\0' && name[len] != '/')
     return 0;
 
   if (__glibc_unlikely (secure)
-      && ((name[len] != '\0' && name[len] != '/'
-          && (!is_path || name[len] != ':'))
-         || (name != start + 1 && (!is_path || name[-2] != ':'))))
+      && ((name[len] != '\0' && name[len] != '/')
+         || (name != start + 1)))
     return 0;
 
   return len;
@@ -221,7 +217,7 @@ is_dst (const char *start, const char *name, const char *str,
 
 
 size_t
-_dl_dst_count (const char *name, int is_path)
+_dl_dst_count (const char *name)
 {
   const char *const start = name;
   size_t cnt = 0;
@@ -233,10 +229,9 @@ _dl_dst_count (const char *name, int is_path)
       /* $ORIGIN is not expanded for SUID/GUID programs (except if it
         is $ORIGIN alone) and it must always appear first in path.  */
       ++name;
-      if ((len = is_dst (start, name, "ORIGIN", is_path,
-                        __libc_enable_secure)) != 0
-         || (len = is_dst (start, name, "PLATFORM", is_path, 0)) != 0
-         || (len = is_dst (start, name, "LIB", is_path, 0)) != 0)
+      if ((len = is_dst (start, name, "ORIGIN", __libc_enable_secure)) != 0
+         || (len = is_dst (start, name, "PLATFORM", 0)) != 0
+         || (len = is_dst (start, name, "LIB", 0)) != 0)
        ++cnt;
 
       name = strchr (name + len, '$');
@@ -248,8 +243,7 @@ _dl_dst_count (const char *name, int is_path)
 
 
 char *
-_dl_dst_substitute (struct link_map *l, const char *name, char *result,
-                   int is_path)
+_dl_dst_substitute (struct link_map *l, const char *name, char *result)
 {
   const char *const start = name;
 
@@ -269,16 +263,15 @@ _dl_dst_substitute (struct link_map *l, const char *name, char *result,
          size_t len;
 
          ++name;
-         if ((len = is_dst (start, name, "ORIGIN", is_path,
-                            __libc_enable_secure)) != 0)
+         if ((len = is_dst (start, name, "ORIGIN", __libc_enable_secure)) != 0)
            {
              repl = l->l_origin;
              check_for_trusted = (__libc_enable_secure
                                   && l->l_type == lt_executable);
            }
-         else if ((len = is_dst (start, name, "PLATFORM", is_path, 0)) != 0)
+         else if ((len = is_dst (start, name, "PLATFORM", 0)) != 0)
            repl = GLRO(dl_platform);
-         else if ((len = is_dst (start, name, "LIB", is_path, 0)) != 0)
+         else if ((len = is_dst (start, name, "LIB", 0)) != 0)
            repl = DL_DST_LIB;
 
          if (repl != NULL && repl != (const char *) -1)
@@ -291,13 +284,7 @@ _dl_dst_substitute (struct link_map *l, const char *name, char *result,
              /* We cannot use this path element, the value of the
                 replacement is unknown.  */
              wp = last_elem;
-             name += len;
-             while (*name != '\0' && (!is_path || *name != ':'))
-               ++name;
-             /* Also skip following colon if this is the first rpath
-                element, but keep an empty element at the end.  */
-             if (wp == result && is_path && *name == ':' && name[1] != '\0')
-               ++name;
+             break;
            }
          else
            /* No DST we recognize.  */
@@ -306,19 +293,6 @@ _dl_dst_substitute (struct link_map *l, const char *name, char *result,
       else
        {
          *wp++ = *name++;
-         if (is_path && *name == ':')
-           {
-             /* In SUID/SGID programs, after $ORIGIN expansion the
-                normalized path must be rooted in one of the trusted
-                directories.  */
-             if (__glibc_unlikely (check_for_trusted)
-                 && !is_trusted_path_normalize (last_elem, wp - last_elem))
-               wp = last_elem;
-             else
-               last_elem = wp;
-
-             check_for_trusted = false;
-           }
        }
     }
   while (*name != '\0');
@@ -341,7 +315,7 @@ _dl_dst_substitute (struct link_map *l, const char *name, char *result,
    belonging to the map is loaded.  In this case the path element
    containing $ORIGIN is left out.  */
 static char *
-expand_dynamic_string_token (struct link_map *l, const char *s, int is_path)
+expand_dynamic_string_token (struct link_map *l, const char *s)
 {
   /* We make two runs over the string.  First we determine how large the
      resulting string is and then we copy it over.  Since this is no
@@ -352,7 +326,7 @@ expand_dynamic_string_token (struct link_map *l, const char *s, int is_path)
   char *result;
 
   /* Determine the number of DST elements.  */
-  cnt = DL_DST_COUNT (s, is_path);
+  cnt = DL_DST_COUNT (s);
 
   /* If we do not have to replace anything simply copy the string.  */
   if (__glibc_likely (cnt == 0))
@@ -366,7 +340,7 @@ expand_dynamic_string_token (struct link_map *l, const char *s, int is_path)
   if (result == NULL)
     return NULL;
 
-  return _dl_dst_substitute (l, s, result, is_path);
+  return _dl_dst_substitute (l, s, result);
 }
 
 
@@ -414,31 +388,40 @@ fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep,
 {
   char *cp;
   size_t nelems = 0;
-  char *to_free;
 
   while ((cp = __strsep (&rpath, sep)) != NULL)
     {
       struct r_search_path_elem *dirp;
+      char *to_free = NULL;
+      size_t len = 0;
 
-      to_free = cp = expand_dynamic_string_token (l, cp, 1);
+      /* `strsep' can pass an empty string.  */
+      if (*cp != '\0')
+       {
+         to_free = cp = expand_dynamic_string_token (l, cp);
 
-      size_t len = strlen (cp);
+         /* expand_dynamic_string_token can return NULL in case of empty
+            path or memory allocation failure.  */
+         if (cp == NULL)
+           continue;
 
-      /* `strsep' can pass an empty string.  This has to be
-        interpreted as `use the current directory'. */
-      if (len == 0)
-       {
-         static const char curwd[] = "./";
-         cp = (char *) curwd;
-       }
+         /* Compute the length after dynamic string token expansion and
+            ignore empty paths.  */
+         len = strlen (cp);
+         if (len == 0)
+           {
+             free (to_free);
+             continue;
+           }
 
-      /* Remove trailing slashes (except for "/").  */
-      while (len > 1 && cp[len - 1] == '/')
-       --len;
+         /* Remove trailing slashes (except for "/").  */
+         while (len > 1 && cp[len - 1] == '/')
+           --len;
 
-      /* Now add one if there is none so far.  */
-      if (len > 0 && cp[len - 1] != '/')
-       cp[len++] = '/';
+         /* Now add one if there is none so far.  */
+         if (len > 0 && cp[len - 1] != '/')
+           cp[len++] = '/';
+       }
 
       /* See if this directory is already known.  */
       for (dirp = GL(dl_all_dirs); dirp != NULL; dirp = dirp->next)
@@ -515,7 +498,6 @@ decompose_rpath (struct r_search_path_struct *sps,
 {
   /* Make a copy we can work with.  */
   const char *where = l->l_name;
-  char *copy;
   char *cp;
   struct r_search_path_elem **result;
   size_t nelems;
@@ -554,22 +536,21 @@ decompose_rpath (struct r_search_path_struct *sps,
       while (*inhp != '\0');
     }
 
+  /* Ignore empty rpaths.  */
+  if (*rpath == '\0')
+    {
+      sps->dirs = (struct r_search_path_elem **) -1;
+      return false;
+    }
+
   /* Make a writable copy.  */
-  copy = __strdup (rpath);
+  char *copy = __strdup (rpath);
   if (copy == NULL)
     {
       errstring = N_("cannot create RUNPATH/RPATH copy");
       goto signal_error;
     }
 
-  /* Ignore empty rpaths.  */
-  if (*copy == 0)
-    {
-      free (copy);
-      sps->dirs = (struct r_search_path_elem **) -1;
-      return false;
-    }
-
   /* Count the number of necessary elements in the result array.  */
   nelems = 0;
   for (cp = copy; *cp != '\0'; ++cp)
@@ -594,6 +575,14 @@ decompose_rpath (struct r_search_path_struct *sps,
      necessary.  */
   free (copy);
 
+  /* There is no path after expansion.  */
+  if (result[0] == NULL)
+    {
+      free (result);
+      sps->dirs = (struct r_search_path_elem **) -1;
+      return false;
+    }
+
   sps->dirs = result;
   /* The caller will change this value if we haven't used a real malloc.  */
   sps->malloced = 1;
@@ -1193,12 +1182,6 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
 
   if (__glibc_unlikely ((stack_flags &~ GL(dl_stack_flags)) & PF_X))
     {
-      if (__glibc_unlikely (__check_caller (RETURN_ADDRESS (0), allow_ldso) != 0))
-       {
-         errstring = N_("invalid caller");
-         goto call_lose;
-       }
-
       /* The stack is presently not executable, but this module
         requires that it be executable.  We must change the
         protection of the variable which contains the flags used in
@@ -1479,6 +1462,7 @@ open_verify (const char *name, int fd,
       ElfW(Ehdr) *ehdr;
       ElfW(Phdr) *phdr, *ph;
       ElfW(Word) *abi_note;
+      ElfW(Word) *abi_note_malloced = NULL;
       unsigned int osversion;
       size_t maplength;
 
@@ -1650,10 +1634,25 @@ open_verify (const char *name, int fd,
              abi_note = (void *) (fbp->buf + ph->p_offset);
            else
              {
-               abi_note = alloca (size);
+               /* Note: __libc_use_alloca is not usable here, because
+                  thread info may not have been set up yet.  */
+               if (size < __MAX_ALLOCA_CUTOFF)
+                 abi_note = alloca (size);
+               else
+                 {
+                   /* There could be multiple PT_NOTEs.  */
+                   abi_note_malloced = realloc (abi_note_malloced, size);
+                   if (abi_note_malloced == NULL)
+                     goto read_error;
+
+                   abi_note = abi_note_malloced;
+                 }
                __lseek (fd, ph->p_offset, SEEK_SET);
                if (__libc_read (fd, (void *) abi_note, size) != size)
-                 goto read_error;
+                 {
+                   free (abi_note_malloced);
+                   goto read_error;
+                 }
              }
 
            while (memcmp (abi_note, &expected_note, sizeof (expected_note)))
@@ -1688,6 +1687,7 @@ open_verify (const char *name, int fd,
 
            break;
          }
+      free (abi_note_malloced);
     }
 
   return fd;
@@ -2091,7 +2091,7 @@ _dl_map_object (struct link_map *loader, const char *name,
     {
       /* The path may contain dynamic string tokens.  */
       realname = (loader
-                 ? expand_dynamic_string_token (loader, name, 0)
+                 ? expand_dynamic_string_token (loader, name)
                  : __strdup (name));
       if (realname == NULL)
        fd = -1;