12b68e
commit TBD
12b68e
Author: Florian Weimer <fweimer@redhat.com>
12b68e
Date:   Fri May 19 17:46:47 2017 +0200
12b68e
12b68e
    rtld: Reject overly long LD_PRELOAD path elements
12b68e
12b68e
Index: b/elf/rtld.c
12b68e
===================================================================
12b68e
--- a/elf/rtld.c
12b68e
+++ b/elf/rtld.c
12b68e
@@ -99,6 +99,22 @@ uintptr_t __pointer_chk_guard_local
12b68e
 strong_alias (__pointer_chk_guard_local, __pointer_chk_guard)
12b68e
 #endif
12b68e
 
12b68e
+/* Check that AT_SECURE=0, or that the passed name does not contain
12b68e
+   directories and is not overly long.  Reject empty names
12b68e
+   unconditionally.  */
12b68e
+static bool
12b68e
+dso_name_valid_for_suid (const char *p)
12b68e
+{
12b68e
+  if (__builtin_expect (INTUSE(__libc_enable_secure), 0))
12b68e
+    {
12b68e
+      /* Ignore pathnames with directories for AT_SECURE=1
12b68e
+	 programs, and also skip overlong names.  */
12b68e
+      size_t len = strlen (p);
12b68e
+      if (len >= NAME_MAX || memchr (p, '/', len) != NULL)
12b68e
+	return false;
12b68e
+    }
12b68e
+  return *p != '\0';
12b68e
+}
12b68e
 
12b68e
 /* List of auditing DSOs.  */
12b68e
 static struct audit_list
12b68e
@@ -880,6 +896,44 @@ static const char *preloadlist attribute
12b68e
 /* Nonzero if information about versions has to be printed.  */
12b68e
 static int version_info attribute_relro;
12b68e
 
12b68e
+/* The LD_PRELOAD environment variable gives list of libraries
12b68e
+   separated by white space or colons that are loaded before the
12b68e
+   executable's dependencies and prepended to the global scope list.
12b68e
+   (If the binary is running setuid all elements containing a '/' are
12b68e
+   ignored since it is insecure.)  Return the number of preloads
12b68e
+   performed.  */
12b68e
+unsigned int
12b68e
+handle_ld_preload (const char *preloadlist, struct link_map *main_map)
12b68e
+{
12b68e
+  unsigned int npreloads = 0;
12b68e
+  const char *p = preloadlist;
12b68e
+  char fname[PATH_MAX];
12b68e
+
12b68e
+  while (*p != '\0')
12b68e
+    {
12b68e
+      /* Split preload list at space/colon.  */
12b68e
+      size_t len = strcspn (p, " :");
12b68e
+      if (len > 0 && len < PATH_MAX)
12b68e
+	{
12b68e
+	  memcpy (fname, p, len);
12b68e
+	  fname[len] = '\0';
12b68e
+	}
12b68e
+      else
12b68e
+	fname[0] = '\0';
12b68e
+
12b68e
+      /* Skip over the substring and the following delimiter.  */
12b68e
+      p += len;
12b68e
+      if (*p == ' ' || *p == ':')
12b68e
+	++p;
12b68e
+
12b68e
+      if (dso_name_valid_for_suid (fname))
12b68e
+	npreloads += do_preload (fname, main_map, "LD_PRELOAD");
12b68e
+    }
12b68e
+  return npreloads;
12b68e
+}
12b68e
+
12b68e
+
12b68e
+
12b68e
 static void
12b68e
 dl_main (const ElfW(Phdr) *phdr,
12b68e
 	 ElfW(Word) phnum,
12b68e
@@ -1611,23 +1665,8 @@ ERROR: ld.so: object '%s' cannot be load
12b68e
 
12b68e
   if (__builtin_expect (preloadlist != NULL, 0))
12b68e
     {
12b68e
-      /* The LD_PRELOAD environment variable gives list of libraries
12b68e
-	 separated by white space or colons that are loaded before the
12b68e
-	 executable's dependencies and prepended to the global scope
12b68e
-	 list.  If the binary is running setuid all elements
12b68e
-	 containing a '/' are ignored since it is insecure.  */
12b68e
-      char *list = strdupa (preloadlist);
12b68e
-      char *p;
12b68e
-
12b68e
       HP_TIMING_NOW (start);
12b68e
-
12b68e
-      /* Prevent optimizing strsep.  Speed is not important here.  */
12b68e
-      while ((p = (strsep) (&list, " :")) != NULL)
12b68e
-	if (p[0] != '\0'
12b68e
-	    && (__builtin_expect (! INTUSE(__libc_enable_secure), 1)
12b68e
-		|| strchr (p, '/') == NULL))
12b68e
-	  npreloads += do_preload (p, main_map, "LD_PRELOAD");
12b68e
-
12b68e
+      npreloads += handle_ld_preload (preloadlist, main_map);
12b68e
       HP_TIMING_NOW (stop);
12b68e
       HP_TIMING_DIFF (diff, start, stop);
12b68e
       HP_TIMING_ACCUM_NT (load_time, diff);