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