summary refs log tree commit diff
path: root/pkgs/development/libraries/glibc/2.19/cve-2014-0475.patch
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/development/libraries/glibc/2.19/cve-2014-0475.patch')
-rw-r--r--pkgs/development/libraries/glibc/2.19/cve-2014-0475.patch170
1 files changed, 170 insertions, 0 deletions
diff --git a/pkgs/development/libraries/glibc/2.19/cve-2014-0475.patch b/pkgs/development/libraries/glibc/2.19/cve-2014-0475.patch
new file mode 100644
index 00000000000..a4f983de8f6
--- /dev/null
+++ b/pkgs/development/libraries/glibc/2.19/cve-2014-0475.patch
@@ -0,0 +1,170 @@
+Picked from upstream commits, but excluding changes to news and tests:
+d183645616b0533 and 4e8f95a0df7c2
+Also see https://sourceware.org/bugzilla/show_bug.cgi?id=17137
+
+diff --git a/locale/setlocale.c b/locale/setlocale.c
+index 9458468..6455b8b 100644
+--- a/locale/setlocale.c
++++ b/locale/setlocale.c
+@@ -272,6 +272,8 @@ setlocale (int category, const char *locale)
+ 	 of entries of the form `CATEGORY=VALUE'.  */
+       const char *newnames[__LC_LAST];
+       struct __locale_data *newdata[__LC_LAST];
++      /* Copy of the locale argument, for in-place splitting.  */
++      char *locale_copy = NULL;
+ 
+       /* Set all name pointers to the argument name.  */
+       for (category = 0; category < __LC_LAST; ++category)
+@@ -281,7 +283,13 @@ setlocale (int category, const char *locale)
+       if (__glibc_unlikely (strchr (locale, ';') != NULL))
+ 	{
+ 	  /* This is a composite name.  Make a copy and split it up.  */
+-	  char *np = strdupa (locale);
++	  locale_copy = strdup (locale);
++	  if (__glibc_unlikely (locale_copy == NULL))
++	    {
++	      __libc_rwlock_unlock (__libc_setlocale_lock);
++	      return NULL;
++	    }
++	  char *np = locale_copy;
+ 	  char *cp;
+ 	  int cnt;
+ 
+@@ -299,6 +307,7 @@ setlocale (int category, const char *locale)
+ 		{
+ 		error_return:
+ 		  __libc_rwlock_unlock (__libc_setlocale_lock);
++		  free (locale_copy);
+ 
+ 		  /* Bogus category name.  */
+ 		  ERROR_RETURN;
+@@ -391,8 +400,9 @@ setlocale (int category, const char *locale)
+       /* Critical section left.  */
+       __libc_rwlock_unlock (__libc_setlocale_lock);
+ 
+-      /* Free the resources (the locale path variable).  */
++      /* Free the resources.  */
+       free (locale_path);
++      free (locale_copy);
+ 
+       return composite;
+     }
+diff --git a/locale/findlocale.c b/locale/findlocale.c
+index bbaf708..22e8b53 100644
+--- a/locale/findlocale.c
++++ b/locale/findlocale.c
+@@ -17,6 +17,7 @@
+    <http://www.gnu.org/licenses/>.  */
+ 
+ #include <assert.h>
++#include <errno.h>
+ #include <locale.h>
+ #include <stdlib.h>
+ #include <string.h>
+@@ -57,6 +58,45 @@ struct loaded_l10nfile *_nl_locale_file_list[__LC_LAST];
+ 
+ const char _nl_default_locale_path[] attribute_hidden = LOCALEDIR;
+ 
++/* Checks if the name is actually present, that is, not NULL and not
++   empty.  */
++static inline int
++name_present (const char *name)
++{
++  return name != NULL && name[0] != '\0';
++}
++
++/* Checks that the locale name neither extremely long, nor contains a
++   ".." path component (to prevent directory traversal).  */
++static inline int
++valid_locale_name (const char *name)
++{
++  /* Not set.  */
++  size_t namelen = strlen (name);
++  /* Name too long.  The limit is arbitrary and prevents stack overflow
++     issues later.  */
++  if (__glibc_unlikely (namelen > 255))
++    return 0;
++  /* Directory traversal attempt.  */
++  static const char slashdot[4] = {'/', '.', '.', '/'};
++  if (__glibc_unlikely (memmem (name, namelen,
++				slashdot, sizeof (slashdot)) != NULL))
++    return 0;
++  if (namelen == 2 && __glibc_unlikely (name[0] == '.' && name [1] == '.'))
++    return 0;
++  if (namelen >= 3
++      && __glibc_unlikely (((name[0] == '.'
++			     && name[1] == '.'
++			     && name[2] == '/')
++			    || (name[namelen - 3] == '/'
++				&& name[namelen - 2] == '.'
++				&& name[namelen - 1] == '.'))))
++    return 0;
++  /* If there is a slash in the name, it must start with one.  */
++  if (__glibc_unlikely (memchr (name, '/', namelen) != NULL) && name[0] != '/')
++    return 0;
++  return 1;
++}
+ 
+ struct __locale_data *
+ internal_function
+@@ -65,7 +105,7 @@ _nl_find_locale (const char *locale_path, size_t locale_path_len,
+ {
+   int mask;
+   /* Name of the locale for this category.  */
+-  char *loc_name;
++  char *loc_name = (char *) *name;
+   const char *language;
+   const char *modifier;
+   const char *territory;
+@@ -73,31 +113,39 @@ _nl_find_locale (const char *locale_path, size_t locale_path_len,
+   const char *normalized_codeset;
+   struct loaded_l10nfile *locale_file;
+ 
+-  if ((*name)[0] == '\0')
++  if (loc_name[0] == '\0')
+     {
+       /* The user decides which locale to use by setting environment
+ 	 variables.  */
+-      *name = getenv ("LC_ALL");
+-      if (*name == NULL || (*name)[0] == '\0')
+-	*name = getenv (_nl_category_names.str
++      loc_name = getenv ("LC_ALL");
++      if (!name_present (loc_name))
++	loc_name = getenv (_nl_category_names.str
+ 			+ _nl_category_name_idxs[category]);
+-      if (*name == NULL || (*name)[0] == '\0')
+-	*name = getenv ("LANG");
++      if (!name_present (loc_name))
++	loc_name = getenv ("LANG");
++      if (!name_present (loc_name))
++	loc_name = (char *) _nl_C_name;
+     }
+ 
+-  if (*name == NULL || (*name)[0] == '\0'
+-      || (__builtin_expect (__libc_enable_secure, 0)
+-	  && strchr (*name, '/') != NULL))
+-    *name = (char *) _nl_C_name;
++  /* We used to fall back to the C locale if the name contains a slash
++     character '/', but we now check for directory traversal in
++     valid_locale_name, so this is no longer necessary.  */
+ 
+-  if (__builtin_expect (strcmp (*name, _nl_C_name), 1) == 0
+-      || __builtin_expect (strcmp (*name, _nl_POSIX_name), 1) == 0)
++  if (__builtin_expect (strcmp (loc_name, _nl_C_name), 1) == 0
++      || __builtin_expect (strcmp (loc_name, _nl_POSIX_name), 1) == 0)
+     {
+       /* We need not load anything.  The needed data is contained in
+ 	 the library itself.  */
+       *name = (char *) _nl_C_name;
+       return _nl_C[category];
+     }
++  else if (!valid_locale_name (loc_name))
++    {
++      __set_errno (EINVAL);
++      return NULL;
++    }
++
++  *name = loc_name;
+ 
+   /* We really have to load some data.  First we try the archive,
+      but only if there was no LOCPATH environment variable specified.  */