]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
For b/4074041, add EXEC_ORIGIN support. Forward-ported from cl/56955623 and http...
authorPaul Pluzhnikov <ppluzhnikov@google.com>
Sat, 8 Mar 2014 23:12:52 +0000 (15:12 -0800)
committerPaul Pluzhnikov <ppluzhnikov@google.com>
Sat, 8 Mar 2014 23:12:52 +0000 (15:12 -0800)
elf/dl-dst.h
elf/dl-load.c
elf/dl-support.c
elf/rtld.c
sysdeps/generic/ldsodefs.h

index 32de5d225a47ab35142c825520766b403b995dc5..f00c8e2a6b42e21093344383a4eec109d70f14a1 100644 (file)
        else                                                                  \
          dst_len = (l)->l_origin == (char *) -1                              \
            ? 0 : strlen ((l)->l_origin);                                     \
-       dst_len = MAX (MAX (dst_len, GLRO(dl_platformlen)),                   \
-                      strlen (DL_DST_LIB));                                  \
+                                                                              \
+       char *exec_origin = GLRO(google_exec_origin_dir);                     \
+       size_t exec_origin_len =                                              \
+         (exec_origin == NULL) ? 0 : strlen (exec_origin);                   \
+                                                                             \
+       dst_len = MAX (MAX (MAX (dst_len, GLRO(dl_platformlen)),              \
+                           strlen (DL_DST_LIB)), exec_origin_len);           \
        if (dst_len > 4)                                                      \
          __len += __cnt * (dst_len - 4);                                     \
       }                                                                              \
index 7554a99b5acb153762ab8967f330309ba9c69821..50cbed2d22946a824cb704384cff3276234d0c11 100644 (file)
@@ -232,7 +232,8 @@ _dl_dst_count (const char *name)
       ++name;
       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)
+         || (len = is_dst (start, name, "LIB", 0)) != 0
+         || (len = is_dst (start, name, "EXEC_ORIGIN", 0)) != 0)
        ++cnt;
 
       name = strchr (name + len, '$');
@@ -274,6 +275,13 @@ _dl_dst_substitute (struct link_map *l, const char *name, char *result)
            repl = GLRO(dl_platform);
          else if ((len = is_dst (start, name, "LIB", 0)) != 0)
            repl = DL_DST_LIB;
+         else if ((len = is_dst (start, name, "EXEC_ORIGIN", is_path, 0)) != 0)
+           {
+             if (INTUSE(__libc_enable_secure) != 0)
+               _dl_fatal_printf ("$EXEC_ORIGIN rpath entry not allowed in setuid/setgid executables.\n");
+
+             repl = GLRO(google_exec_origin_dir);
+           }
 
          if (repl != NULL && repl != (const char *) -1)
            {
index 114f77a8e5553565de2100ce891147070cc58e3c..febc5e2f708fdb1d0f030641e4895b98c548de40 100644 (file)
@@ -67,6 +67,9 @@ void *__libc_stack_end;
 /* Path where the binary is found.  */
 const char *_dl_origin_path;
 
+/* Directory where the AT_EXECFN is found.  */
+const char *_google_exec_origin_dir;
+
 /* Nonzero if runtime lookup should not update the .got/.plt.  */
 int _dl_bind_not;
 
index 9040d7477e90e8176ecac30dd51485f35ac350e7..4491f1a6b04401fc9d6d119a5dd66ad05580ed65 100644 (file)
@@ -51,6 +51,12 @@ extern __typeof (__mempcpy) __mempcpy attribute_hidden;
 extern __typeof (_exit) exit_internal asm ("_exit") attribute_hidden;
 #define _exit exit_internal
 
+/* Iterate over auxv, find AT_EXECFN if any.  */
+static char * get_at_execfn(ElfW(auxv_t) *auxv);
+
+/* Given file path, return fully resolved directory path.  */
+static char * get_directory (const char *file_path);
+
 /* Helper function to handle errors while resolving symbols.  */
 static void print_unresolved (int errcode, const char *objname,
                              const char *errsting);
@@ -73,6 +79,9 @@ enum mode { normal, list, verify, trace };
    all the entries.  */
 static void process_envvars (enum mode *modep);
 
+/* Set GLRO(google_exec_origin_dir).  */
+static void set_exec_origin_dir(const char *exe_path);
+
 #ifdef DL_ARGV_NOT_RELRO
 int _dl_argc attribute_hidden;
 char **_dl_argv = NULL;
@@ -1032,6 +1041,8 @@ of this helper program; chances are you did not intend to run this program.\n\
                        in LIST\n\
   --audit LIST          use objects named in LIST as auditors\n");
 
+      set_exec_origin_dir (INTUSE(_dl_argv)[1]);
+
       ++_dl_skip_args;
       --_dl_argc;
       ++_dl_argv;
@@ -1126,6 +1137,8 @@ of this helper program; chances are you did not intend to run this program.\n\
     }
   else
     {
+      set_exec_origin_dir (get_at_execfn (auxv));
+
       /* Create a link_map for the executable itself.
         This will be what dlopen on "" returns.  */
       main_map = _dl_new_object ((char *) "", "", lt_executable, NULL,
@@ -2811,6 +2824,81 @@ print_statistics (hp_timing_t *rtld_total_timep)
 #endif
 }
 
+/* Given file path, return an absolute directory path.
+   Examples: in: "/foo/bar/a.out", out: "/foo/bar/";
+   in: "./a.out", out: "/dot/resolved/to/full/path/./".  */
+static char *
+get_directory (const char *file_path)
+{
+  assert (file_path != NULL);
+
+  /* Find the end of the directory substring in file_path.  */
+  size_t path_len = strlen (file_path);
+  while (path_len > 0 && file_path[path_len - 1] != '/')
+    --path_len;
+
+  /* Allocate space and set the path prefix according to whether or not
+     this is an absolute path.  */
+  char *dest;
+  char *full_dir_path;
+  if (file_path[0] == '/')
+    {
+      full_dir_path = malloc (path_len + 1);
+      assert (full_dir_path != NULL);
+      dest = full_dir_path;
+    }
+  else
+    {
+      /* For a relative path, we need to include space for the largest
+        possible current path, a joining '/', the relevant part of
+        file_path, and a trailing '\0'.  */
+      full_dir_path = malloc (PATH_MAX + path_len + 2);
+      assert (full_dir_path != NULL);
+
+      char *status = __getcwd (full_dir_path, PATH_MAX);
+      assert (status != NULL);
+
+      dest = __rawmemchr (full_dir_path, '\0');
+      if (dest[-1] != '/')
+       *dest++ = '/';
+    }
+
+  if (path_len > 0)
+    dest = __mempcpy (dest, file_path, path_len);
+  *dest = '\0';
+
+  /* Confirm that the constructed path is valid.  */
+  struct stat64 st;
+  assert (__xstat64 (_STAT_VER, full_dir_path, &st) == 0);
+
+  return full_dir_path;
+}
+
+/* Set GLRO(google_exec_origin_dir).  */
+static void
+set_exec_origin_dir (const char *exe_path)
+{
+  assert (GLRO(google_exec_origin_dir) == NULL);
+
+  if (GLRO(dl_origin_path) != NULL)
+    GLRO(google_exec_origin_dir) = strdup (GLRO(dl_origin_path));
+  else if (exe_path != NULL)
+    GLRO(google_exec_origin_dir) = get_directory (exe_path);
+}
+
+/* Iterate over auxv, find AT_EXECFN if any.  */
+static char *
+get_at_execfn (ElfW(auxv_t) *auxv)
+{
+  assert (auxv != NULL);
+
+  for (; auxv->a_type != AT_NULL; ++auxv)
+    if (auxv->a_type == AT_EXECFN)
+      return (char *) auxv->a_un.a_val;
+
+  return NULL;
+}
+
 #ifndef NESTING
 char *dummy1 = (char *)elf_get_dynamic_info;
 # if ! ELF_MACHINE_NO_REL
index a9c2b0835a7783962ebbd513f30d7dc144cf8ba5..caa5f1da4c70c689706a682b3c976bbf2a00275e 100644 (file)
@@ -544,6 +544,9 @@ struct rtld_global_ro
   /* Location of the binary.  */
   EXTERN const char *_dl_origin_path;
 
+  /* Directory where the AT_EXECFN is found.  */
+  EXTERN const char *_google_exec_origin_dir;
+
   /* -1 if the dynamic linker should honor library load bias,
      0 if not, -2 use the default (honor biases for normal
      binaries, don't honor for PIEs).  */