]> git.ipfire.org Git - people/pmueller/ipfire-2.x.git/commitdiff
glibc: Backport hotfixes from RHEL
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 27 Jan 2015 21:01:24 +0000 (22:01 +0100)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 27 Jan 2015 21:01:24 +0000 (22:01 +0100)
12 files changed:
lfs/glibc
src/patches/glibc/glibc-rh1019916.patch [new file with mode: 0644]
src/patches/glibc/glibc-rh1027101.patch [moved from src/patches/glibc/glibc-rh1091162.patch with 100% similarity]
src/patches/glibc/glibc-rh1027261.patch [new file with mode: 0644]
src/patches/glibc/glibc-rh1032628.patch [new file with mode: 0644]
src/patches/glibc/glibc-rh1044628.patch [moved from src/patches/glibc/glibc-rh1098050.patch with 100% similarity]
src/patches/glibc/glibc-rh1111460.patch [new file with mode: 0644]
src/patches/glibc/glibc-rh1139571.patch [new file with mode: 0644]
src/patches/glibc/glibc-rh1154563.patch [new file with mode: 0644]
src/patches/glibc/glibc-rh1170121.patch [new file with mode: 0644]
src/patches/glibc/glibc-rh1183533.patch [new file with mode: 0644]
src/patches/glibc/glibc-rh995972.patch [new file with mode: 0644]

index df3e39224f749f264de68ed2cdcacc659d2bc840..11d374e3b886a13fabdfbfca70309112c22ef3e7 100644 (file)
--- a/lfs/glibc
+++ b/lfs/glibc
@@ -268,12 +268,21 @@ endif
        cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh966775.patch
        cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh966778.patch
        cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh970090.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh995972.patch
        cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh1008310.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh1019916.patch
        cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh1022022.patch
-       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh1091162.patch
-       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh1098050.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh1027101.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh1027261.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh1032628.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh1044628.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh1111460.patch
        cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh1133809-1.patch
        cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh1133809-2.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh1139571.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh1154563.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh1170121.patch
+       cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc/glibc-rh1183533.patch
 
        cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-resolv-stack_chk_fail.patch
        cd $(DIR_APP) && patch -Np1 < $(DIR_SRC)/src/patches/glibc-remove-ctors-dtors-output-sections.patch
diff --git a/src/patches/glibc/glibc-rh1019916.patch b/src/patches/glibc/glibc-rh1019916.patch
new file mode 100644 (file)
index 0000000..f67af90
--- /dev/null
@@ -0,0 +1,39 @@
+commit 48b67d71ec677d1b3168e52a68b644784cead604
+Author: Andreas Schwab <schwab@redhat.com>
+Date:   Wed Sep 14 12:12:25 2011 +0200
+
+    Also relocate in dependency order when doing symbol dependency testing
+
+diff --git a/elf/rtld.c b/elf/rtld.c
+index 764140d..324d979 100644
+--- a/elf/rtld.c
++++ b/elf/rtld.c
+@@ -2027,24 +2027,21 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
+           {
+             /* We have to do symbol dependency testing.  */
+             struct relocate_args args;
+-            struct link_map *l;
++            unsigned int i;
+             args.reloc_mode = GLRO(dl_lazy) ? RTLD_LAZY : 0;
+-            l = main_map;
+-            while (l->l_next != NULL)
+-              l = l->l_next;
+-            do
++            i = main_map->l_searchlist.r_nlist;
++            while (i-- > 0)
+               {
++                struct link_map *l = main_map->l_initfini[i];
+                 if (l != &GL(dl_rtld_map) && ! l->l_faked)
+                   {
+                     args.l = l;
+                     _dl_receive_error (print_unresolved, relocate_doit,
+                                        &args);
+                   }
+-                l = l->l_prev;
+               }
+-            while (l != NULL);
+             if ((GLRO(dl_debug_mask) & DL_DEBUG_PRELINK)
+                 && rtld_multiple_ref)
diff --git a/src/patches/glibc/glibc-rh1027261.patch b/src/patches/glibc/glibc-rh1027261.patch
new file mode 100644 (file)
index 0000000..8599cf0
--- /dev/null
@@ -0,0 +1,28 @@
+commit 4d653a59ffeae0f46f76a40230e2cfa9587b7e7e
+Author: Siddhesh Poyarekar <siddhesh@redhat.com>
+Date:   Fri May 30 22:43:52 2014 +0530
+
+    Add mmap usage in malloc_info output
+    
+    The current malloc_info xml output only has information about
+    allocations on the heap.  Display information about number of mappings
+    and total mmapped size to this to complete the picture.
+
+diff -pruN a/malloc/malloc.c b/malloc/malloc.c
+--- a/malloc/malloc.c  2014-06-02 07:35:22.573256155 +0530
++++ b/malloc/malloc.c  2014-06-02 07:34:58.856257177 +0530
+@@ -6553,12 +6553,14 @@ malloc_info (int options, FILE *fp)
+   fprintf (fp,
+          "<total type=\"fast\" count=\"%zu\" size=\"%zu\"/>\n"
+          "<total type=\"rest\" count=\"%zu\" size=\"%zu\"/>\n"
++         "<total type=\"mmap\" count=\"%d\" size=\"%zu\"/>\n"
+          "<system type=\"current\" size=\"%zu\"/>\n"
+          "<system type=\"max\" size=\"%zu\"/>\n"
+          "<aspace type=\"total\" size=\"%zu\"/>\n"
+          "<aspace type=\"mprotect\" size=\"%zu\"/>\n"
+          "</malloc>\n",
+          total_nfastblocks, total_fastavail, total_nblocks, total_avail,
++         mp_.n_mmaps, mp_.mmapped_mem,
+          total_system, total_max_system,
+          total_aspace, total_aspace_mprotect);
diff --git a/src/patches/glibc/glibc-rh1032628.patch b/src/patches/glibc/glibc-rh1032628.patch
new file mode 100644 (file)
index 0000000..6140c19
--- /dev/null
@@ -0,0 +1,166 @@
+commit 028478fa40d85a73b19638dbe3f83b1acebf370c
+Author: Ulrich Drepper <drepper@gmail.com>
+Date:   Thu Mar 10 12:51:33 2011 -0500
+
+    Fix copy relocations handling of unique objects.
+
+ 2011-03-06  Ulrich Drepper  <drepper@gmail.com>
+
+and a part of:
+
+commit 33f85a3fb9fe432e0ebf6a3481bc2d5e29cb605f
+Author: Ulrich Drepper <drepper@gmail.com>
+Date:   Thu Mar 10 03:18:21 2011 -0500
+
+    Don't run tests checking xecutable stack when SELinux is enforcing.
+
+since the latter incorrectly had a bit of the former changes.
+
+Additionally, the test case needs -lstdc++ to build.
+
+diff --git a/elf/Makefile b/elf/Makefile
+index c427679..56cb1b1 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -201,7 +201,7 @@ tests += loadtest restest1 preloadtest loadfail multiload origtest resolvfail \
+        unload3 unload4 unload5 unload6 unload7 unload8 tst-global1 order2 \
+        tst-audit1 tst-audit2 tst-audit9 \
+        tst-stackguard1 tst-addr1 tst-thrlock \
+-       tst-unique1 tst-unique2
++       tst-unique1 tst-unique2 tst-unique3
+ #      reldep9
+ test-srcs = tst-pathopt
+ tests-execstack-yes = tst-execstack tst-execstack-needed tst-execstack-prog
+@@ -255,6 +255,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
+               order2mod1 order2mod2 order2mod3 order2mod4 \
+               tst-unique1mod1 tst-unique1mod2 \
+               tst-unique2mod1 tst-unique2mod2 \
++              tst-unique3lib tst-unique3lib2 \
+               tst-auditmod9a tst-auditmod9b
+ ifeq (yes,$(have-initfini-array))
+ modules-names += tst-array2dep tst-array5dep
+@@ -1178,6 +1179,11 @@ $(objpfx)tst-unique1.out: $(objpfx)tst-unique1mod1.so \
+ $(objpfx)tst-unique2: $(libdl) $(objpfx)tst-unique2mod1.so
+ $(objpfx)tst-unique2.out: $(objpfx)tst-unique2mod2.so
++LDLIBS-tst-unique3lib.so = -lstdc++
++LDLIBS-tst-unique3lib2.so = -lstdc++
++$(objpfx)tst-unique3: $(libdl) $(objpfx)tst-unique3lib.so
++$(objpfx)tst-unique3.out: $(objpfx)tst-unique3lib2.so
++
+ ifeq (yes,$(config-cflags-avx))
+ CFLAGS-tst-audit4.c += -mavx
+ CFLAGS-tst-auditmod4a.c += -mavx
+diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c
+index 78c8669..874a4bb 100644
+--- a/elf/dl-lookup.c
++++ b/elf/dl-lookup.c
+@@ -364,8 +363,19 @@ do_lookup_x (const char *undef_name, uint_fast32_t new_hash,
+                     if (entries[idx].hashval == new_hash
+                         && strcmp (entries[idx].name, undef_name) == 0)
+                       {
+-                        result->s = entries[idx].sym;
+-                        result->m = (struct link_map *) entries[idx].map;
++                        if ((type_class & ELF_RTYPE_CLASS_COPY) != 0)
++                          {
++                            /* We possibly have to initialize the central
++                               copy from the copy addressed through the
++                               relocation.  */
++                            result->s = sym;
++                            result->m = (struct link_map *) map;
++                          }
++                        else
++                          {
++                            result->s = entries[idx].sym;
++                            result->m = (struct link_map *) entries[idx].map;
++                          }
+                         __rtld_lock_unlock_recursive (tab->lock);
+                         return 1;
+                       }
+diff --git a/elf/tst-unique3.cc b/elf/tst-unique3.cc
+new file mode 100644
+index 0000000..b2c9593
+--- /dev/null
++++ b/elf/tst-unique3.cc
+@@ -0,0 +1,23 @@
++#include "tst-unique3.h"
++#include <cstdio>
++#include "../dlfcn/dlfcn.h"
++
++int t = S<char>::i;
++
++int
++main (void)
++{
++  std::printf ("%d %d\n", S<char>::i, t);
++  int result = S<char>::i++ != 1 || t != 1;
++  result |= in_lib ();
++  void *d = dlopen ("$ORIGIN/tst-unique3lib2.so", RTLD_LAZY);
++  int (*fp) ();
++  if (d == NULL || (fp = (int(*)()) dlsym (d, "in_lib2")) == NULL)
++    {
++      std::printf ("failed to get symbol in_lib2\n");
++      return 1;
++    }
++  result |= fp ();
++  dlclose (d);
++  return result;
++}
+diff --git a/elf/tst-unique3.h b/elf/tst-unique3.h
+new file mode 100644
+index 0000000..716d236
+--- /dev/null
++++ b/elf/tst-unique3.h
+@@ -0,0 +1,8 @@
++// BZ 12510
++template<typename T>
++struct S
++{
++  static int i;
++};
++
++extern int in_lib (void);
+diff --git a/elf/tst-unique3lib.cc b/elf/tst-unique3lib.cc
+new file mode 100644
+index 0000000..fa8e85a
+--- /dev/null
++++ b/elf/tst-unique3lib.cc
+@@ -0,0 +1,11 @@
++#include <cstdio>
++#include "tst-unique3.h"
++template<typename T> int S<T>::i = 1;
++static int i = S<char>::i;
++
++int
++in_lib (void)
++{
++  std::printf ("in_lib: %d %d\n", S<char>::i, i);
++  return S<char>::i++ != 2 || i != 1;
++}
+diff --git a/elf/tst-unique3lib2.cc b/elf/tst-unique3lib2.cc
+new file mode 100644
+index 0000000..17d817e
+--- /dev/null
++++ b/elf/tst-unique3lib2.cc
+@@ -0,0 +1,12 @@
++#include <cstdio>
++#include "tst-unique3.h"
++
++template<typename T> int S<T>::i;
++
++extern "C"
++int
++in_lib2 ()
++{
++  std::printf ("in_lib2: %d\n", S<char>::i);
++  return S<char>::i != 3;
++}
+diff --git a/include/bits/dlfcn.h b/include/bits/dlfcn.h
+index cb4a5c2..c31a645 100644
+--- a/include/bits/dlfcn.h
++++ b/include/bits/dlfcn.h
+@@ -1,4 +1,3 @@
+ #include_next <bits/dlfcn.h>
+-extern void _dl_mcount_wrapper_check (void *__selfpc);
+ libc_hidden_proto (_dl_mcount_wrapper_check)
diff --git a/src/patches/glibc/glibc-rh1111460.patch b/src/patches/glibc/glibc-rh1111460.patch
new file mode 100644 (file)
index 0000000..1a4315d
--- /dev/null
@@ -0,0 +1,341 @@
+commit 7cbcdb3699584db8913ca90f705d6337633ee10f
+Author: Siddhesh Poyarekar <siddhesh@redhat.com>
+Date:   Fri Oct 25 10:22:12 2013 +0530
+
+    Fix stack overflow due to large AF_INET6 requests
+    
+    Resolves #16072 (CVE-2013-4458).
+    
+    This patch fixes another stack overflow in getaddrinfo when it is
+    called with AF_INET6.  The AF_UNSPEC case was fixed as CVE-2013-1914,
+    but the AF_INET6 case went undetected back then.
+
+commit 91ce40854d0b7f865cf5024ef95a8026b76096f3
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Fri Aug 16 09:38:52 2013 +0200
+
+    CVE-2013-4237, BZ #14699: Buffer overflow in readdir_r
+    
+       * sysdeps/posix/dirstream.h (struct __dirstream): Add errcode
+       member.
+       * sysdeps/posix/opendir.c (__alloc_dir): Initialize errcode
+       member.
+       * sysdeps/posix/rewinddir.c (rewinddir): Reset errcode member.
+       * sysdeps/posix/readdir_r.c (__READDIR_R): Enforce NAME_MAX limit.
+       Return delayed error code.  Remove GETDENTS_64BIT_ALIGNED
+       conditional.
+       * sysdeps/unix/sysv/linux/wordsize-64/readdir_r.c: Do not define
+       GETDENTS_64BIT_ALIGNED.
+       * sysdeps/unix/sysv/linux/i386/readdir64_r.c: Likewise.
+       * manual/filesys.texi (Reading/Closing Directory): Document
+       ENAMETOOLONG return value of readdir_r.  Recommend readdir more
+       strongly.
+       * manual/conf.texi (Limits for Files): Add portability note to
+       NAME_MAX, PATH_MAX.
+       (Pathconf): Add portability note for _PC_NAME_MAX, _PC_PATH_MAX.
+
+diff --git a/sysdeps/posix/getaddrinfo.c b/sysdeps/posix/getaddrinfo.c
+index e6ce4cf..8ff74b4 100644
+--- a/sysdeps/posix/getaddrinfo.c
++++ b/sysdeps/posix/getaddrinfo.c
+@@ -197,7 +197,22 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
+                               &rc, &herrno, NULL, &localcanon));            \
+     if (rc != ERANGE || herrno != NETDB_INTERNAL)                           \
+       break;                                                                \
+-    tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen);                      \
++    if (!malloc_tmpbuf && __libc_use_alloca (alloca_used + 2 * tmpbuflen))    \
++      tmpbuf = extend_alloca_account (tmpbuf, tmpbuflen, 2 * tmpbuflen,             \
++                                    alloca_used);                           \
++    else                                                                    \
++      {                                                                             \
++      char *newp = realloc (malloc_tmpbuf ? tmpbuf : NULL,                  \
++                            2 * tmpbuflen);                                 \
++      if (newp == NULL)                                                     \
++        {                                                                   \
++          result = -EAI_MEMORY;                                             \
++          goto free_and_return;                                             \
++        }                                                                   \
++      tmpbuf = newp;                                                        \
++      malloc_tmpbuf = true;                                                 \
++      tmpbuflen = 2 * tmpbuflen;                                            \
++      }                                                                             \
+   }                                                                         \
+   if (status == NSS_STATUS_SUCCESS && rc == 0)                                      \
+     h = &th;                                                                \
+@@ -209,7 +224,8 @@ gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
+       {                                                                     \
+         __set_h_errno (herrno);                                             \
+         _res.options = old_res_options;                                     \
+-        return -EAI_SYSTEM;                                                 \
++        result = -EAI_SYSTEM;                                               \
++        goto free_and_return;                                               \
+       }                                                                     \
+       if (herrno == TRY_AGAIN)                                                      \
+       no_data = EAI_AGAIN;                                                  \
+
+diff --git a/manual/conf.texi b/manual/conf.texi
+index 7eb8b36..c720063 100644
+--- a/manual/conf.texi
++++ b/manual/conf.texi
+@@ -1149,6 +1149,9 @@ typed ahead as input.  @xref{I/O Queues}.
+ @comment POSIX.1
+ @deftypevr Macro int NAME_MAX
+ The uniform system limit (if any) for the length of a file name component.
++
++@strong{Portability Note:} On some systems, the GNU C Library defines
++@code{NAME_MAX}, but does not actually enforce this limit.
+ @end deftypevr
+ @comment limits.h
+@@ -1157,6 +1160,9 @@ including the terminating null character.
+ @deftypevr Macro int PATH_MAX
+ The uniform system limit (if any) for the length of an entire file name (that
+ is, the argument given to system calls such as @code{open}).
++
++@strong{Portability Note:} The GNU C Library does not enforce this limit
++even if @code{PATH_MAX} is defined.
+ @end deftypevr
+ @cindex limits, pipe buffer size
+@@ -1476,6 +1482,9 @@ Inquire about the value of @code{POSIX_REC_MIN_XFER_SIZE}.
+ Inquire about the value of @code{POSIX_REC_XFER_ALIGN}.
+ @end table
++@strong{Portability Note:} On some systems, the GNU C Library does not
++enforce @code{_PC_NAME_MAX} or @code{_PC_PATH_MAX} limits.
++
+ @node Utility Limits
+ @section Utility Program Capacity Limits
+diff --git a/manual/filesys.texi b/manual/filesys.texi
+index 1df9cf2..814c210 100644
+--- a/manual/filesys.texi
++++ b/manual/filesys.texi
+@@ -444,9 +444,9 @@ symbols are declared in the header file @file{dirent.h}.
+ @comment POSIX.1
+ @deftypefun {struct dirent *} readdir (DIR *@var{dirstream})
+ This function reads the next entry from the directory.  It normally
+-returns a pointer to a structure containing information about the file.
+-This structure is statically allocated and can be rewritten by a
+-subsequent call.
++returns a pointer to a structure containing information about the
++file.  This structure is associated with the @var{dirstream} handle
++and can be rewritten by a subsequent call.
+ @strong{Portability Note:} On some systems @code{readdir} may not
+ return entries for @file{.} and @file{..}, even though these are always
+@@ -461,19 +461,61 @@ conditions are defined for this function:
+ The @var{dirstream} argument is not valid.
+ @end table
+-@code{readdir} is not thread safe.  Multiple threads using
+-@code{readdir} on the same @var{dirstream} may overwrite the return
+-value.  Use @code{readdir_r} when this is critical.
++To distinguish between an end-of-directory condition or an error, you
++must set @code{errno} to zero before calling @code{readdir}.  To avoid
++entering an infinite loop, you should stop reading from the directory
++after the first error.
++
++In POSIX.1-2008, @code{readdir} is not thread-safe.  In the GNU C Library
++implementation, it is safe to call @code{readdir} concurrently on
++different @var{dirstream}s, but multiple threads accessing the same
++@var{dirstream} result in undefined behavior.  @code{readdir_r} is a
++fully thread-safe alternative, but suffers from poor portability (see
++below).  It is recommended that you use @code{readdir}, with external
++locking if multiple threads access the same @var{dirstream}.
+ @end deftypefun
+ @comment dirent.h
+ @comment GNU
+ @deftypefun int readdir_r (DIR *@var{dirstream}, struct dirent *@var{entry}, struct dirent **@var{result})
+-This function is the reentrant version of @code{readdir}.  Like
+-@code{readdir} it returns the next entry from the directory.  But to
+-prevent conflicts between simultaneously running threads the result is
+-not stored in statically allocated memory.  Instead the argument
+-@var{entry} points to a place to store the result.
++This function is a version of @code{readdir} which performs internal
++locking.  Like @code{readdir} it returns the next entry from the
++directory.  To prevent conflicts between simultaneously running
++threads the result is stored inside the @var{entry} object.
++
++@strong{Portability Note:} It is recommended to use @code{readdir}
++instead of @code{readdir_r} for the following reasons:
++
++@itemize @bullet
++@item
++On systems which do not define @code{NAME_MAX}, it may not be possible
++to use @code{readdir_r} safely because the caller does not specify the
++length of the buffer for the directory entry.
++
++@item
++On some systems, @code{readdir_r} cannot read directory entries with
++very long names.  If such a name is encountered, the GNU C Library
++implementation of @code{readdir_r} returns with an error code of
++@code{ENAMETOOLONG} after the final directory entry has been read.  On
++other systems, @code{readdir_r} may return successfully, but the
++@code{d_name} member may not be NUL-terminated or may be truncated.
++
++@item
++POSIX-1.2008 does not guarantee that @code{readdir} is thread-safe,
++even when access to the same @var{dirstream} is serialized.  But in
++current implementations (including the GNU C Library), it is safe to call
++@code{readdir} concurrently on different @var{dirstream}s, so there is
++no need to use @code{readdir_r} in most multi-threaded programs.  In
++the rare case that multiple threads need to read from the same
++@var{dirstream}, it is still better to use @code{readdir} and external
++synchronization.
++
++@item
++It is expected that future versions of POSIX will obsolete
++@code{readdir_r} and mandate the level of thread safety for
++@code{readdir} which is provided by the GNU C Library and other
++implementations today.
++@end itemize
+ Normally @code{readdir_r} returns zero and sets @code{*@var{result}}
+ to @var{entry}.  If there are no more entries in the directory or an
+@@ -481,15 +523,6 @@ error is detected, @code{readdir_r} sets @code{*@var{result}} to a
+ null pointer and returns a nonzero error code, also stored in
+ @code{errno}, as described for @code{readdir}.
+-@strong{Portability Note:} On some systems @code{readdir_r} may not
+-return a NUL terminated string for the file name, even when there is no
+-@code{d_reclen} field in @code{struct dirent} and the file
+-name is the maximum allowed size.  Modern systems all have the
+-@code{d_reclen} field, and on old systems multi-threading is not
+-critical.  In any case there is no such problem with the @code{readdir}
+-function, so that even on systems without the @code{d_reclen} member one
+-could use multiple threads by using external locking.
+-
+ It is also important to look at the definition of the @code{struct
+ dirent} type.  Simply passing a pointer to an object of this type for
+ the second parameter of @code{readdir_r} might not be enough.  Some
+diff --git a/sysdeps/unix/dirstream.h b/sysdeps/unix/dirstream.h
+index a7a074d..8e8570d 100644
+--- a/sysdeps/unix/dirstream.h
++++ b/sysdeps/unix/dirstream.h
+@@ -39,6 +39,8 @@ struct __dirstream
+     off_t filepos;            /* Position of next entry to read.  */
++    int errcode;              /* Delayed error code.  */
++
+     /* Directory block.  */
+     char data[0] __attribute__ ((aligned (__alignof__ (void*))));
+   };
+diff --git a/sysdeps/unix/opendir.c b/sysdeps/unix/opendir.c
+index ddfc3a7..fc05b0f 100644
+--- a/sysdeps/unix/opendir.c
++++ b/sysdeps/unix/opendir.c
+@@ -231,6 +231,7 @@ __alloc_dir (int fd, bool close_fd, int flags, const struct stat64 *statp)
+   dirp->size = 0;
+   dirp->offset = 0;
+   dirp->filepos = 0;
++  dirp->errcode = 0;
+   return dirp;
+ }
+diff --git a/sysdeps/unix/readdir_r.c b/sysdeps/unix/readdir_r.c
+index b5a8e2e..8ed5c3f 100644
+--- a/sysdeps/unix/readdir_r.c
++++ b/sysdeps/unix/readdir_r.c
+@@ -40,6 +40,7 @@ __READDIR_R (DIR *dirp, DIRENT_TYPE *entry, DIRENT_TYPE **result)
+   DIRENT_TYPE *dp;
+   size_t reclen;
+   const int saved_errno = errno;
++  int ret;
+   __libc_lock_lock (dirp->lock);
+@@ -70,10 +71,10 @@ __READDIR_R (DIR *dirp, DIRENT_TYPE *entry, DIRENT_TYPE **result)
+                 bytes = 0;
+                 __set_errno (saved_errno);
+               }
++            if (bytes < 0)
++              dirp->errcode = errno;
+             dp = NULL;
+-            /* Reclen != 0 signals that an error occurred.  */
+-            reclen = bytes != 0;
+             break;
+           }
+         dirp->size = (size_t) bytes;
+@@ -106,28 +107,46 @@ __READDIR_R (DIR *dirp, DIRENT_TYPE *entry, DIRENT_TYPE **result)
+       dirp->filepos += reclen;
+ #endif
+-      /* Skip deleted files.  */
++#ifdef NAME_MAX
++      if (reclen > offsetof (DIRENT_TYPE, d_name) + NAME_MAX + 1)
++      {
++        /* The record is very long.  It could still fit into the
++           caller-supplied buffer if we can skip padding at the
++           end.  */
++        size_t namelen = _D_EXACT_NAMLEN (dp);
++        if (namelen <= NAME_MAX)
++          reclen = offsetof (DIRENT_TYPE, d_name) + namelen + 1;
++        else
++          {
++            /* The name is too long.  Ignore this file.  */
++            dirp->errcode = ENAMETOOLONG;
++            dp->d_ino = 0;
++            continue;
++          }
++      }
++#endif
++
++      /* Skip deleted and ignored files.  */
+     }
+   while (dp->d_ino == 0);
+   if (dp != NULL)
+     {
+-#ifdef GETDENTS_64BIT_ALIGNED
+-      /* The d_reclen value might include padding which is not part of
+-       the DIRENT_TYPE data structure.  */
+-      reclen = MIN (reclen, sizeof (DIRENT_TYPE));
+-#endif
+       *result = memcpy (entry, dp, reclen);
+-#ifdef GETDENTS_64BIT_ALIGNED
++#ifdef _DIRENT_HAVE_D_RECLEN
+       entry->d_reclen = reclen;
+ #endif
++      ret = 0;
+     }
+   else
+-    *result = NULL;
++    {
++      *result = NULL;
++      ret = dirp->errcode;
++    }
+   __libc_lock_unlock (dirp->lock);
+-  return dp != NULL ? 0 : reclen ? errno : 0;
++  return ret;
+ }
+ #ifdef __READDIR_R_ALIAS
+diff --git a/sysdeps/unix/rewinddir.c b/sysdeps/unix/rewinddir.c
+index 2935a8e..d4991ad 100644
+--- a/sysdeps/unix/rewinddir.c
++++ b/sysdeps/unix/rewinddir.c
+@@ -33,5 +33,6 @@ rewinddir (dirp)
+   dirp->filepos = 0;
+   dirp->offset = 0;
+   dirp->size = 0;
++  dirp->errcode = 0;
+   __libc_lock_unlock (dirp->lock);
+ }
+diff --git a/sysdeps/unix/sysv/linux/i386/readdir64_r.c b/sysdeps/unix/sysv/linux/i386/readdir64_r.c
+index 8ebbcfd..a7d114e 100644
+--- a/sysdeps/unix/sysv/linux/i386/readdir64_r.c
++++ b/sysdeps/unix/sysv/linux/i386/readdir64_r.c
+@@ -18,7 +18,6 @@
+ #define __READDIR_R __readdir64_r
+ #define __GETDENTS __getdents64
+ #define DIRENT_TYPE struct dirent64
+-#define GETDENTS_64BIT_ALIGNED 1
+ #include <sysdeps/unix/readdir_r.c>
diff --git a/src/patches/glibc/glibc-rh1139571.patch b/src/patches/glibc/glibc-rh1139571.patch
new file mode 100644 (file)
index 0000000..b1320a7
--- /dev/null
@@ -0,0 +1,154 @@
+commit 41488498b6d9440ee66ab033808cce8323bba7ac
+Author: Florian Weimer <fweimer@redhat.com>
+Date:   Wed Sep 3 19:45:43 2014 +0200
+
+    CVE-2014-6040: Crashes on invalid input in IBM gconv modules [BZ #17325]
+    
+    These changes are based on the fix for BZ #14134 in commit
+    6e230d11837f3ae7b375ea69d7905f0d18eb79e5.
+
+diff --git a/iconvdata/Makefile b/iconvdata/Makefile
+index 0a410a1..b6327d6 100644
+--- a/iconvdata/Makefile
++++ b/iconvdata/Makefile
+@@ -297,6 +297,7 @@ $(objpfx)tst-iconv7.out: $(objpfx)gconv-modules \
+ $(objpfx)iconv-test.out: run-iconv-test.sh $(objpfx)gconv-modules \
+                        $(addprefix $(objpfx),$(modules.so)) \
+                        $(common-objdir)/iconv/iconv_prog TESTS
++      iconv_modules="$(modules)" \
+       $(SHELL) -e $< $(common-objdir) > $@
+ $(objpfx)tst-tables.out: tst-tables.sh $(objpfx)gconv-modules \
+diff --git a/iconvdata/ibm1364.c b/iconvdata/ibm1364.c
+index 0b5484f..cf80993 100644
+--- a/iconvdata/ibm1364.c
++++ b/iconvdata/ibm1364.c
+@@ -221,7 +221,8 @@ enum
+         ++rp2;                                                              \
+                                                                             \
+       uint32_t res;                                                         \
+-      if (__builtin_expect (ch < rp2->start, 0)                             \
++      if (__builtin_expect (rp2->start == 0xffff, 0)                        \
++          || __builtin_expect (ch < rp2->start, 0)                          \
+           || (res = DB_TO_UCS4[ch + rp2->idx],                              \
+               __builtin_expect (res, L'\1') == L'\0' && ch != '\0'))        \
+         {                                                                   \
+diff --git a/iconvdata/ibm932.c b/iconvdata/ibm932.c
+index f5dca59..aa69d65 100644
+--- a/iconvdata/ibm932.c
++++ b/iconvdata/ibm932.c
+@@ -74,11 +74,12 @@
+         }                                                                   \
+                                                                             \
+       ch = (ch * 0x100) + inptr[1];                                         \
++      /* ch was less than 0xfd.  */                                         \
++      assert (ch < 0xfd00);                                                 \
+       while (ch > rp2->end)                                                 \
+         ++rp2;                                                              \
+                                                                             \
+-      if (__builtin_expect (rp2 == NULL, 0)                                 \
+-          || __builtin_expect (ch < rp2->start, 0)                          \
++      if (__builtin_expect (ch < rp2->start, 0)                             \
+           || (res = __ibm932db_to_ucs4[ch + rp2->idx],                      \
+           __builtin_expect (res, '\1') == 0 && ch !=0))                     \
+         {                                                                   \
+diff --git a/iconvdata/ibm933.c b/iconvdata/ibm933.c
+index f46dfb5..461fb5e 100644
+--- a/iconvdata/ibm933.c
++++ b/iconvdata/ibm933.c
+@@ -162,7 +162,7 @@ enum
+       while (ch > rp2->end)                                                 \
+         ++rp2;                                                              \
+                                                                             \
+-      if (__builtin_expect (rp2 == NULL, 0)                                 \
++      if (__builtin_expect (rp2->start == 0xffff, 0)                        \
+           || __builtin_expect (ch < rp2->start, 0)                          \
+           || (res = __ibm933db_to_ucs4[ch + rp2->idx],                      \
+               __builtin_expect (res, L'\1') == L'\0' && ch != '\0'))        \
+diff --git a/iconvdata/ibm935.c b/iconvdata/ibm935.c
+index a8e4e6c..132d816 100644
+--- a/iconvdata/ibm935.c
++++ b/iconvdata/ibm935.c
+@@ -162,7 +162,7 @@ enum
+       while (ch > rp2->end)                                                 \
+         ++rp2;                                                              \
+                                                                             \
+-      if (__builtin_expect (rp2 == NULL, 0)                                 \
++      if (__builtin_expect (rp2->start == 0xffff, 0)                        \
+           || __builtin_expect (ch < rp2->start, 0)                          \
+           || (res = __ibm935db_to_ucs4[ch + rp2->idx],                      \
+               __builtin_expect (res, L'\1') == L'\0' && ch != '\0'))        \
+diff --git a/iconvdata/ibm937.c b/iconvdata/ibm937.c
+index 239be61..69b154d 100644
+--- a/iconvdata/ibm937.c
++++ b/iconvdata/ibm937.c
+@@ -162,7 +162,7 @@ enum
+       while (ch > rp2->end)                                                 \
+         ++rp2;                                                              \
+                                                                             \
+-      if (__builtin_expect (rp2 == NULL, 0)                                 \
++      if (__builtin_expect (rp2->start == 0xffff, 0)                        \
+           || __builtin_expect (ch < rp2->start, 0)                          \
+           || (res = __ibm937db_to_ucs4[ch + rp2->idx],                      \
+               __builtin_expect (res, L'\1') == L'\0' && ch != '\0'))        \
+diff --git a/iconvdata/ibm939.c b/iconvdata/ibm939.c
+index 5d0db36..9936e2c 100644
+--- a/iconvdata/ibm939.c
++++ b/iconvdata/ibm939.c
+@@ -162,7 +162,7 @@ enum
+       while (ch > rp2->end)                                                 \
+         ++rp2;                                                              \
+                                                                             \
+-      if (__builtin_expect (rp2 == NULL, 0)                                 \
++      if (__builtin_expect (rp2->start == 0xffff, 0)                        \
+           || __builtin_expect (ch < rp2->start, 0)                          \
+           || (res = __ibm939db_to_ucs4[ch + rp2->idx],                      \
+               __builtin_expect (res, L'\1') == L'\0' && ch != '\0'))        \
+diff --git a/iconvdata/ibm943.c b/iconvdata/ibm943.c
+index be0c14f..c5d5742 100644
+--- a/iconvdata/ibm943.c
++++ b/iconvdata/ibm943.c
+@@ -75,11 +75,12 @@
+         }                                                                   \
+                                                                             \
+       ch = (ch * 0x100) + inptr[1];                                         \
++      /* ch was less than 0xfd.  */                                         \
++      assert (ch < 0xfd00);                                                 \
+       while (ch > rp2->end)                                                 \
+         ++rp2;                                                              \
+                                                                             \
+-      if (__builtin_expect (rp2 == NULL, 0)                                 \
+-          || __builtin_expect (ch < rp2->start, 0)                          \
++      if (__builtin_expect (ch < rp2->start, 0)                             \
+           || (res = __ibm943db_to_ucs4[ch + rp2->idx],                      \
+           __builtin_expect (res, '\1') == 0 && ch !=0))                     \
+         {                                                                   \
+diff --git a/iconvdata/run-iconv-test.sh b/iconvdata/run-iconv-test.sh
+index c98c929..5dfb69f 100755
+--- a/iconvdata/run-iconv-test.sh
++++ b/iconvdata/run-iconv-test.sh
+@@ -184,6 +184,24 @@ while read utf8 from filename; do
+ done < TESTS2
++# Check for crashes in decoders.
++printf '\016\377\377\377\377\377\377\377' > $temp1
++for from in $iconv_modules ; do
++    echo $ac_n "test decoder $from $ac_c"
++    PROG=`eval echo $ICONV`
++    if $PROG < $temp1 >/dev/null 2>&1 ; then
++      : # fall through
++    else
++      status=$?
++      if test $status -gt 1 ; then
++          echo "/FAILED"
++          failed=1
++          continue
++      fi
++    fi
++    echo "OK"
++done
++
+ exit $failed
+ # Local Variables:
+ #  mode:shell-script
diff --git a/src/patches/glibc/glibc-rh1154563.patch b/src/patches/glibc/glibc-rh1154563.patch
new file mode 100644 (file)
index 0000000..22821b1
--- /dev/null
@@ -0,0 +1,333 @@
+#
+# This is a special patch for rhel-6 to fix recursive dlopen.
+# It is likely the upstream patch will always be too risky for
+# rhel-6 and will involve reorganizing the way in which recursive
+# dlopen is allowed to operate and how the _r_debug and stap
+# points are used by gdb for the recursive case.
+#
+# This fix changes the internal API to duplicate the ldconfig
+# cache data. This means that at any point the cache can be
+# unmapped without any consequences. The caller is responsible
+# fore freeing the returned string.
+#
+# A regression test is added to verify the assertion for _r_debug
+# is no longer triggered due to the recursive dlopen. The test to
+# verify the fix in _dl_load_cache_lookup is not automated and
+# has to be run by hand.
+#
+diff -urN glibc-2.12-2-gc4ccff1/elf/dl-cache.c glibc-2.12-2-gc4ccff1.mod/elf/dl-cache.c
+--- glibc-2.12-2-gc4ccff1/elf/dl-cache.c       2010-05-04 07:27:23.000000000 -0400
++++ glibc-2.12-2-gc4ccff1.mod/elf/dl-cache.c   2014-12-10 21:54:08.801985045 -0500
+@@ -175,9 +175,12 @@
+ /* Look up NAME in ld.so.cache and return the file name stored there,
+-   or null if none is found.  */
+-
+-const char *
++   or null if none is found. 
++   The caller is responsible for freeing the returned string.  The ld.so.cache
++   may be unmapped at any time by a completing recursive dlopen and
++   this function must take care that it does not return references to
++   any data in the mapping.  */
++char *
+ internal_function
+ _dl_load_cache_lookup (const char *name)
+ {
+@@ -290,7 +293,17 @@
+       && best != NULL)
+     _dl_debug_printf ("  trying file=%s\n", best);
+-  return best;
++  if (best == NULL)
++    return NULL;
++
++  /* The double copy is *required* since malloc may be interposed
++     and call dlopen itself whose completion would unmap the data
++     we are accessing. Therefore we must make the copy of the
++     mapping data without using malloc.  */
++  char *temp;
++  temp = alloca (strlen (best) + 1);
++  strcpy (temp, best);
++  return strdup (temp);
+ }
+ #ifndef MAP_COPY
+diff -urN glibc-2.12-2-gc4ccff1/elf/dl-load.c glibc-2.12-2-gc4ccff1.mod/elf/dl-load.c
+--- glibc-2.12-2-gc4ccff1/elf/dl-load.c        2014-12-10 11:03:17.966048404 -0500
++++ glibc-2.12-2-gc4ccff1.mod/elf/dl-load.c    2014-12-10 21:47:29.319387538 -0500
+@@ -2126,7 +2126,7 @@
+       {
+         /* Check the list of libraries in the file /etc/ld.so.cache,
+            for compatibility with Linux's ldconfig program.  */
+-        const char *cached = _dl_load_cache_lookup (name);
++        char *cached = _dl_load_cache_lookup (name);
+         if (cached != NULL)
+           {
+@@ -2156,6 +2156,7 @@
+                     if (memcmp (cached, dirp, system_dirs_len[cnt]) == 0)
+                       {
+                         /* The prefix matches.  Don't use the entry.  */
++                        free (cached);
+                         cached = NULL;
+                         break;
+                       }
+@@ -2172,14 +2173,9 @@
+                                   &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded,
+                                   LA_SER_CONFIG, &found_other_class, false);
+                 if (__builtin_expect (fd != -1, 1))
+-                  {
+-                    realname = local_strdup (cached);
+-                    if (realname == NULL)
+-                      {
+-                        __close (fd);
+-                        fd = -1;
+-                      }
+-                  }
++                  realname = cached;
++                else
++                  free (cached);
+               }
+           }
+       }
+diff -urN glibc-2.12-2-gc4ccff1/elf/dl-open.c glibc-2.12-2-gc4ccff1.mod/elf/dl-open.c
+--- glibc-2.12-2-gc4ccff1/elf/dl-open.c        2014-12-10 11:03:18.083048497 -0500
++++ glibc-2.12-2-gc4ccff1.mod/elf/dl-open.c    2014-12-10 20:34:16.017503638 -0500
+@@ -220,7 +220,11 @@
+       }
+     }
+-  assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
++  /* One might be tempted to assert that we are RT_CONSISTENT at this point, but that
++     may not be true if this is a recursive call to dlopen.
++     TODO: Fix all of the debug state so we end up at RT_CONSISTENT only when the last
++     recursive dlopen completes.  */
++  _dl_debug_initialize (0, args->nsid);
+   /* Load the named object.  */
+   struct link_map *new;
+diff -urN glibc-2.12-2-gc4ccff1/sysdeps/generic/ldsodefs.h glibc-2.12-2-gc4ccff1.mod/sysdeps/generic/ldsodefs.h
+--- glibc-2.12-2-gc4ccff1/sysdeps/generic/ldsodefs.h   2014-12-10 11:03:17.944048387 -0500
++++ glibc-2.12-2-gc4ccff1.mod/sysdeps/generic/ldsodefs.h       2014-12-10 21:46:14.071344018 -0500
+@@ -996,8 +996,8 @@
+      internal_function;
+ /* Look up NAME in ld.so.cache and return the file name stored there,
+-   or null if none is found.  */
+-extern const char *_dl_load_cache_lookup (const char *name)
++   or null if none is found.  Caller must free returned string.  */
++extern char *_dl_load_cache_lookup (const char *name)
+      internal_function;
+ /* If the system does not support MAP_COPY we cannot leave the file open
+diff -urN glibc-2.12-2-gc4ccff1/dlfcn/Makefile glibc-2.12-2-gc4ccff1.mod/dlfcn/Makefile
+--- glibc-2.12-2-gc4ccff1/dlfcn/Makefile       2010-05-04 07:27:23.000000000 -0400
++++ glibc-2.12-2-gc4ccff1.mod/dlfcn/Makefile   2014-12-11 16:58:55.719803063 -0500
+@@ -42,12 +42,12 @@
+ ifeq (yes,$(build-shared))
+ tests = glrefmain failtest tst-dladdr default errmsg1 tstcxaatexit \
+       bug-dlopen1 bug-dlsym1 tst-dlinfo bug-atexit1 bug-atexit2 \
+-      bug-atexit3 tstatexit
++      bug-atexit3 tstatexit tst-rec-dlopen
+ endif
+ modules-names = glreflib1 glreflib2 glreflib3 failtestmod defaultmod1 \
+               defaultmod2 errmsg1mod modatexit modcxaatexit \
+               bug-dlsym1-lib1 bug-dlsym1-lib2 bug-atexit1-lib \
+-              bug-atexit2-lib bug-atexit3-lib
++              bug-atexit2-lib bug-atexit3-lib moddummy1 moddummy2
+ failtestmod.so-no-z-defs = yes
+ glreflib2.so-no-z-defs = yes
+@@ -142,6 +142,8 @@
+ $(objpfx)bug-atexit3-lib.so: $(common-objpfx)libc.so \
+                            $(common-objpfx)libc_nonshared.a
++LDLIBS-tst-rec-dlopen = -ldl
++$(objpfx)tst-rec-dlopen: $(libdl)
+ # Depend on libc.so so a DT_NEEDED is generated in the shared objects.
+ # This ensures they will load libc.so for needed symbols if loaded by
+diff -urN glibc-2.12-2-gc4ccff1/dlfcn/moddummy1.c glibc-2.12-2-gc4ccff1.mod/dlfcn/moddummy1.c
+--- glibc-2.12-2-gc4ccff1/dlfcn/moddummy1.c    1969-12-31 19:00:00.000000000 -0500
++++ glibc-2.12-2-gc4ccff1.mod/dlfcn/moddummy1.c        2014-12-11 16:57:54.108797285 -0500
+@@ -0,0 +1,13 @@
++/* Provide a dummy DSO for tst-recursive-dlopen to use.  */
++#include <stdio.h>
++#include <stdlib.h>
++
++int called_dummy1;
++
++void
++dummy1 (void)
++{
++  printf ("Called dummy1()\n");
++  called_dummy1++;
++}
++
+diff -urN glibc-2.12-2-gc4ccff1/dlfcn/moddummy2.c glibc-2.12-2-gc4ccff1.mod/dlfcn/moddummy2.c
+--- glibc-2.12-2-gc4ccff1/dlfcn/moddummy2.c    1969-12-31 19:00:00.000000000 -0500
++++ glibc-2.12-2-gc4ccff1.mod/dlfcn/moddummy2.c        2014-12-11 16:57:54.108797285 -0500
+@@ -0,0 +1,13 @@
++/* Provide a dummy DSO for tst-recursive-dlopen to use.  */
++#include <stdio.h>
++#include <stdlib.h>
++
++int called_dummy2;
++
++void
++dummy2 (void)
++{
++  printf ("Called dummy2()\n");
++  called_dummy2++;
++}
++
+diff -urN glibc-2.12-2-gc4ccff1/dlfcn/tst-rec-dlopen.c glibc-2.12-2-gc4ccff1.mod/dlfcn/tst-rec-dlopen.c
+--- glibc-2.12-2-gc4ccff1/dlfcn/tst-rec-dlopen.c       1969-12-31 19:00:00.000000000 -0500
++++ glibc-2.12-2-gc4ccff1.mod/dlfcn/tst-rec-dlopen.c   2014-12-11 20:53:28.617848774 -0500
+@@ -0,0 +1,145 @@
++/* Test recursive dlopen using malloc hooks.
++   Copyright (C) 1998-2014 Free Software Foundation, Inc.
++   This file is part of the GNU C Library.
++   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
++
++   The GNU C Library is free software; you can redistribute it and/or
++   modify it under the terms of the GNU Lesser General Public
++   License as published by the Free Software Foundation; either
++   version 2.1 of the License, or (at your option) any later version.
++
++   The GNU C Library is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++   Lesser General Public License for more details.
++
++   You should have received a copy of the GNU Lesser General Public
++   License along with the GNU C Library; if not, see
++   <http://www.gnu.org/licenses/>.  */
++
++#include <stdio.h>
++#include <stdlib.h>
++#include <malloc.h>
++#include <dlfcn.h>
++
++#define DSO "moddummy1.so"
++#define FUNC "dummy1"
++
++#define DSO1 "moddummy2.so"
++#define FUNC1 "dummy2"
++
++/* Prevent the compiler from moving the assignment to called_func
++   before (*func)() since the compiler doesn't know we might abort
++   or catch a SIGSEGV signal and it may move the store.  */
++volatile int called_func;
++
++/* Prototype for my hook.  */
++void *custom_malloc_hook (size_t, const void *);
++
++/* Pointer to old malloc hooks.  */
++void *(*old_malloc_hook) (size_t, const void *);
++
++/* Call function func_name in DSO dso_name via dlopen.  */
++void
++call_func (const char *dso_name, const char *func_name)
++{
++  int ret;
++  void *dso;
++  void (*func) (void);
++  char *err;
++
++  /* Open the DSO.  */
++  dso = dlopen (dso_name, RTLD_NOW|RTLD_GLOBAL);
++  if (dso == NULL)
++    {
++      err = dlerror ();
++      fprintf (stderr, "%s\n", err);
++      exit (1);
++    }
++  /* Clear any errors.  */
++  dlerror ();
++
++  /* Lookup func.  */
++  *(void **) (&func) = dlsym (dso, func_name);
++  if (func == NULL)
++    {
++      err = dlerror ();
++      if (err != NULL)
++        {
++        fprintf (stderr, "%s\n", err);
++        exit (1);
++        }
++    }
++  /* Call func.  */
++  (*func) ();
++  called_func = 1;
++
++  /* Close the library and look for errors too.  */
++  ret = dlclose (dso);
++  if (ret != 0)
++    {
++      err = dlerror ();
++      fprintf (stderr, "%s\n", err);
++      exit (1);
++    }
++
++}
++
++/* Empty hook that does nothing.  */
++void *
++custom_malloc_hook (size_t size, const void *caller)
++{
++  void *result;
++  /* Restore old hooks.  */
++  __malloc_hook = old_malloc_hook;
++  /* First call a function in another library via dlopen.  */
++  call_func (DSO1, FUNC1);
++  /* Called recursively.  */
++  result = malloc (size);
++  /* Restore new hooks.  */
++  __malloc_hook = custom_malloc_hook;
++  return result;
++}
++
++static int
++do_test (void)
++{
++  /* Save old hook.  */
++  old_malloc_hook = __malloc_hook;
++  /* Install new hook.  */
++  __malloc_hook = custom_malloc_hook;
++
++  /* Bug 17702 fixes two things:
++       * A recursive dlopen unmapping the ld.so.cache.
++       * An assertion that _r_debug is RT_CONSISTENT at entry to dlopen.
++     We can only test the latter. Testing the former requires modifying
++     ld.so.conf to cache the dummy libraries, then running ldconfig,
++     then run the test. If you do all of that (and glibc's test
++     infrastructure doesn't support that yet) then the test will
++     SEGFAULT without the fix. If you don't do that, then the test
++     will abort because of the assert described in detail below.  */
++  call_func (DSO, FUNC);
++
++  /* Restore old hook.  */
++  __malloc_hook = old_malloc_hook;
++
++  /* The function dummy2() is called by the malloc hook. Check to
++     see that it was called. This ensures the second recursive
++     dlopen happened and we called the function in that library.
++
++     Before the fix you either get a SIGSEGV when accessing mmap'd
++     ld.so.cache data or an assertion failure about _r_debug not
++     beint RT_CONSISTENT.  We don't test for the SIGSEGV since it
++     would require finding moddummy1 or moddummy2 in the cache and
++     we don't have any infrastructure to test that, but the _r_debug
++     assertion triggers.  */
++  if (called_func > 0)
++    printf ("PASS: Function call_func() called more than once.\n");
++  else
++    printf ("FAIL: Function call_func() not called.\n");
++
++  return 0;
++}
++
++#define TEST_FUNCTION do_test ()
++#include "../test-skeleton.c"
diff --git a/src/patches/glibc/glibc-rh1170121.patch b/src/patches/glibc/glibc-rh1170121.patch
new file mode 100644 (file)
index 0000000..1accbf3
--- /dev/null
@@ -0,0 +1,163 @@
+#
+# commit a39208bd7fb76c1b01c127b4c61f9bfd915bfe7c
+# Author: Carlos O'Donell <carlos@redhat.com>
+# Date:   Wed Nov 19 11:44:12 2014 -0500
+# 
+#     CVE-2014-7817: wordexp fails to honour WRDE_NOCMD.
+#     
+#     The function wordexp() fails to properly handle the WRDE_NOCMD
+#     flag when processing arithmetic inputs in the form of "$((... ``))"
+#     where "..." can be anything valid. The backticks in the arithmetic
+#     epxression are evaluated by in a shell even if WRDE_NOCMD forbade
+#     command substitution. This allows an attacker to attempt to pass
+#     dangerous commands via constructs of the above form, and bypass
+#     the WRDE_NOCMD flag. This patch fixes this by checking for WRDE_NOCMD
+#     in exec_comm(), the only place that can execute a shell. All other
+#     checks for WRDE_NOCMD are superfluous and removed.
+#     
+#     We expand the testsuite and add 3 new regression tests of roughly
+#     the same form but with a couple of nested levels.
+#     
+#     On top of the 3 new tests we add fork validation to the WRDE_NOCMD
+#     testing. If any forks are detected during the execution of a wordexp()
+#     call with WRDE_NOCMD, the test is marked as failed. This is slightly
+#     heuristic since vfork might be used in the future, but it provides a
+#     higher level of assurance that no shells were executed as part of
+#     command substitution with WRDE_NOCMD in effect. In addition it doesn't
+#     require libpthread or libdl, instead we use the public implementation
+#     namespace function __register_atfork (already part of the public ABI
+#     for libpthread).
+#     
+#     Tested on x86_64 with no regressions.
+# 
+diff --git a/posix/wordexp-test.c b/posix/wordexp-test.c
+index 4957006..bdd65e4 100644
+--- a/posix/wordexp-test.c
++++ b/posix/wordexp-test.c
+@@ -27,6 +27,25 @@
+ #define IFS " \n\t"
++extern void *__dso_handle __attribute__ ((__weak__, __visibility__ ("hidden")));
++extern int __register_atfork (void (*) (void), void (*) (void), void (*) (void), void *);
++
++static int __app_register_atfork (void (*prepare) (void), void (*parent) (void), void (*child) (void))
++{
++  return __register_atfork (prepare, parent, child,
++                          &__dso_handle == NULL ? NULL : __dso_handle);
++}
++
++/* Number of forks seen.  */
++static int registered_forks;
++
++/* For each fork increment the fork count.  */
++static void
++register_fork (void)
++{
++  registered_forks++;
++}
++
+ struct test_case_struct
+ {
+   int retval;
+@@ -206,6 +225,12 @@ struct test_case_struct
+     { WRDE_SYNTAX, NULL, "$((2+))", 0, 0, { NULL, }, IFS },
+     { WRDE_SYNTAX, NULL, "`", 0, 0, { NULL, }, IFS },
+     { WRDE_SYNTAX, NULL, "$((010+4+))", 0, 0, { NULL }, IFS },
++    /* Test for CVE-2014-7817. We test 3 combinations of command
++       substitution inside an arithmetic expression to make sure that
++       no commands are executed and error is returned.  */
++    { WRDE_CMDSUB, NULL, "$((`echo 1`))", WRDE_NOCMD, 0, { NULL, }, IFS },
++    { WRDE_CMDSUB, NULL, "$((1+`echo 1`))", WRDE_NOCMD, 0, { NULL, }, IFS },
++    { WRDE_CMDSUB, NULL, "$((1+$((`echo 1`))))", WRDE_NOCMD, 0, { NULL, }, IFS },
+     { -1, NULL, NULL, 0, 0, { NULL, }, IFS },
+   };
+@@ -258,6 +283,15 @@ main (int argc, char *argv[])
+         return -1;
+     }
++  /* If we are not allowed to do command substitution, we install
++     fork handlers to verify that no forks happened.  No forks should
++     happen at all if command substitution is disabled.  */
++  if (__app_register_atfork (register_fork, NULL, NULL) != 0)
++    {
++      printf ("Failed to register fork handler.\n");
++      return -1;
++    }
++
+   for (test = 0; test_case[test].retval != -1; test++)
+     if (testit (&test_case[test]))
+       ++fail;
+@@ -367,6 +401,9 @@ testit (struct test_case_struct *tc)
+   printf ("Test %d (%s): ", ++tests, tc->words);
++  if (tc->flags & WRDE_NOCMD)
++    registered_forks = 0;
++
+   if (tc->flags & WRDE_APPEND)
+     {
+       /* initial wordexp() call, to be appended to */
+@@ -378,6 +415,13 @@ testit (struct test_case_struct *tc)
+     }
+   retval = wordexp (tc->words, &we, tc->flags);
++  if ((tc->flags & WRDE_NOCMD)
++      && (registered_forks > 0))
++    {
++        printf ("FAILED fork called for WRDE_NOCMD\n");
++        return 1;
++    }
++
+   if (tc->flags & WRDE_DOOFFS)
+       start_offs = sav_we.we_offs;
+diff --git a/posix/wordexp.c b/posix/wordexp.c
+index b6b65dd..26f3a26 100644
+--- a/posix/wordexp.c
++++ b/posix/wordexp.c
+@@ -893,6 +893,10 @@ exec_comm (char *comm, char **word, size_t *word_length, size_t *max_length,
+   pid_t pid;
+   int noexec = 0;
++  /* Do nothing if command substitution should not succeed.  */
++  if (flags & WRDE_NOCMD)
++    return WRDE_CMDSUB;
++
+   /* Don't fork() unless necessary */
+   if (!comm || !*comm)
+     return 0;
+@@ -2082,9 +2086,6 @@ parse_dollars (char **word, size_t *word_length, size_t *max_length,
+           }
+       }
+-      if (flags & WRDE_NOCMD)
+-      return WRDE_CMDSUB;
+-
+       (*offset) += 2;
+       return parse_comm (word, word_length, max_length, words, offset, flags,
+                        quoted? NULL : pwordexp, ifs, ifs_white);
+@@ -2196,9 +2197,6 @@ parse_dquote (char **word, size_t *word_length, size_t *max_length,
+         break;
+       case '`':
+-        if (flags & WRDE_NOCMD)
+-          return WRDE_CMDSUB;
+-
+         ++(*offset);
+         error = parse_backtick (word, word_length, max_length, words,
+                                 offset, flags, NULL, NULL, NULL);
+@@ -2357,12 +2355,6 @@ wordexp (const char *words, wordexp_t *pwordexp, int flags)
+       break;
+       case '`':
+-      if (flags & WRDE_NOCMD)
+-        {
+-          error = WRDE_CMDSUB;
+-          goto do_error;
+-        }
+-
+       ++words_offset;
+       error = parse_backtick (&word, &word_length, &max_length, words,
+                               &words_offset, flags, pwordexp, ifs,
diff --git a/src/patches/glibc/glibc-rh1183533.patch b/src/patches/glibc/glibc-rh1183533.patch
new file mode 100644 (file)
index 0000000..9263cd5
--- /dev/null
@@ -0,0 +1,210 @@
+commit d5dd6189d506068ed11c8bfa1e1e9bffde04decd
+Author: Andreas Schwab <schwab@suse.de>
+Date:   Mon Jan 21 17:41:28 2013 +0100
+
+    Fix parsing of numeric hosts in gethostbyname_r
+
+diff --git a/nss/digits_dots.c b/nss/digits_dots.c
+index 2b86295..e007ef4 100644
+--- a/nss/digits_dots.c
++++ b/nss/digits_dots.c
+@@ -46,7 +46,10 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf,
+     {
+       if (h_errnop)
+       *h_errnop = NETDB_INTERNAL;
+-      *result = NULL;
++      if (buffer_size == NULL)
++      *status = NSS_STATUS_TRYAGAIN;
++      else
++      *result = NULL;
+       return -1;
+     }
+@@ -83,14 +86,16 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf,
+       }
+       size_needed = (sizeof (*host_addr)
+-                   + sizeof (*h_addr_ptrs) + strlen (name) + 1);
++                   + sizeof (*h_addr_ptrs)
++                   + sizeof (*h_alias_ptr) + strlen (name) + 1);
+       if (buffer_size == NULL)
+         {
+         if (buflen < size_needed)
+           {
++            *status = NSS_STATUS_TRYAGAIN;
+             if (h_errnop != NULL)
+-              *h_errnop = TRY_AGAIN;
++              *h_errnop = NETDB_INTERNAL;
+             __set_errno (ERANGE);
+             goto done;
+           }
+@@ -109,7 +114,7 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf,
+             *buffer_size = 0;
+             __set_errno (save);
+             if (h_errnop != NULL)
+-              *h_errnop = TRY_AGAIN;
++              *h_errnop = NETDB_INTERNAL;
+             *result = NULL;
+             goto done;
+           }
+@@ -149,7 +154,9 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf,
+                 if (! ok)
+                   {
+                     *h_errnop = HOST_NOT_FOUND;
+-                    if (buffer_size)
++                    if (buffer_size == NULL)
++                      *status = NSS_STATUS_NOTFOUND;
++                    else
+                       *result = NULL;
+                     goto done;
+                   }
+@@ -190,7 +197,7 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf,
+                 if (buffer_size == NULL)
+                   *status = NSS_STATUS_SUCCESS;
+                 else
+-                 *result = resbuf;
++                  *result = resbuf;
+                 goto done;
+               }
+@@ -201,15 +208,6 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf,
+       if ((isxdigit (name[0]) && strchr (name, ':') != NULL) || name[0] == ':')
+       {
+-        const char *cp;
+-        char *hostname;
+-        typedef unsigned char host_addr_t[16];
+-        host_addr_t *host_addr;
+-        typedef char *host_addr_list_t[2];
+-        host_addr_list_t *h_addr_ptrs;
+-        size_t size_needed;
+-        int addr_size;
+-
+         switch (af)
+           {
+           default:
+@@ -225,7 +223,10 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf,
+             /* This is not possible.  We cannot represent an IPv6 address
+                in an `struct in_addr' variable.  */
+             *h_errnop = HOST_NOT_FOUND;
+-            *result = NULL;
++            if (buffer_size == NULL)
++              *status = NSS_STATUS_NOTFOUND;
++            else
++              *result = NULL;
+             goto done;
+           case AF_INET6:
+@@ -233,42 +234,6 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf,
+             break;
+           }
+-        size_needed = (sizeof (*host_addr)
+-                       + sizeof (*h_addr_ptrs) + strlen (name) + 1);
+-
+-        if (buffer_size == NULL && buflen < size_needed)
+-          {
+-            if (h_errnop != NULL)
+-              *h_errnop = TRY_AGAIN;
+-            __set_errno (ERANGE);
+-            goto done;
+-          }
+-        else if (buffer_size != NULL && *buffer_size < size_needed)
+-          {
+-            char *new_buf;
+-            *buffer_size = size_needed;
+-            new_buf = realloc (*buffer, *buffer_size);
+-
+-            if (new_buf == NULL)
+-              {
+-                save = errno;
+-                free (*buffer);
+-                __set_errno (save);
+-                *buffer = NULL;
+-                *buffer_size = 0;
+-                *result = NULL;
+-                goto done;
+-              }
+-            *buffer = new_buf;
+-          }
+-
+-        memset (*buffer, '\0', size_needed);
+-
+-        host_addr = (host_addr_t *) *buffer;
+-        h_addr_ptrs = (host_addr_list_t *)
+-          ((char *) host_addr + sizeof (*host_addr));
+-        hostname = (char *) h_addr_ptrs + sizeof (*h_addr_ptrs);
+-
+         for (cp = name;; ++cp)
+           {
+             if (!*cp)
+@@ -281,7 +246,9 @@ __nss_hostname_digits_dots (const char *name, struct hostent *resbuf,
+                 if (inet_pton (AF_INET6, name, host_addr) <= 0)
+                   {
+                     *h_errnop = HOST_NOT_FOUND;
+-                    if (buffer_size)
++                    if (buffer_size == NULL)
++                      *status = NSS_STATUS_NOTFOUND;
++                    else
+                       *result = NULL;
+                     goto done;
+                   }
+diff --git a/nss/getXXbyYY_r.c b/nss/getXXbyYY_r.c
+index 1067744..44d00f4 100644
+--- a/nss/getXXbyYY_r.c
++++ b/nss/getXXbyYY_r.c
+@@ -179,6 +179,9 @@ INTERNAL (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer,
+     case -1:
+       return errno;
+     case 1:
++#ifdef NEED_H_ERRNO
++      any_service = true;
++#endif
+       goto done;
+     }
+ #endif
+diff --git a/nss/test-digits-dots.c b/nss/test-digits-dots.c
+new file mode 100644
+index 0000000..1efa344
+--- /dev/null
++++ b/nss/test-digits-dots.c
+@@ -0,0 +1,38 @@
++/* Copyright (C) 2013 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
++   modify it under the terms of the GNU Lesser General Public
++   License as published by the Free Software Foundation; either
++   version 2.1 of the License, or (at your option) any later version.
++
++   The GNU C Library is distributed in the hope that it will be useful,
++   but WITHOUT ANY WARRANTY; without even the implied warranty of
++   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
++   Lesser General Public License for more details.
++
++   You should have received a copy of the GNU Lesser General Public
++   License along with the GNU C Library; if not, see
++   <http://www.gnu.org/licenses/>.  */
++
++/* Testcase for BZ #15014 */
++
++#include <stdlib.h>
++#include <netdb.h>
++#include <errno.h>
++
++static int
++do_test (void)
++{
++  char buf[32];
++  struct hostent *result = NULL;
++  struct hostent ret;
++  int h_err = 0;
++  int err;
++
++  err = gethostbyname_r ("1.2.3.4", &ret, buf, sizeof (buf), &result, &h_err);
++  return err == ERANGE && h_err == NETDB_INTERNAL ? EXIT_SUCCESS : EXIT_FAILURE;
++}
++
++#define TEST_FUNCTION do_test ()
++#include "../test-skeleton.c"
diff --git a/src/patches/glibc/glibc-rh995972.patch b/src/patches/glibc/glibc-rh995972.patch
new file mode 100644 (file)
index 0000000..0178bca
--- /dev/null
@@ -0,0 +1,246 @@
+commit d26dfc60edc8c6dd160eefff16a734152a835ca0
+Author: Martin von Gagern <Martin.vGagern@gmx.net>
+Date:   Sat May 14 21:25:43 2011 -0400
+
+    Fix handling of static TLS in dlopen'ed objects
+    
+    When dynamically loading a library along with several dependencies, calls to
+    _dl_add_to_slotinfo and _dl_update_slotinfo can become intermixed. As a
+    consequence, _dl_update_slotinfo will update the generation counter of the dtv
+    although not all of the slots belonging to that generation have been added.
+    Subsequent calls to _dl_add_to_slotinfo will add more slots to the same
+    generation, for which no storage will be allocated, as the dtv generation
+    checks will claim no work is necessary. This will lead to uninitialized dtv
+    entries and will likely cause a SIGSEGV when thread local variables are
+    accessed.
+
+diff --git a/elf/Makefile b/elf/Makefile
+index 8d9657d..6efb86c 100644
+--- a/elf/Makefile
++++ b/elf/Makefile
+@@ -76,6 +76,7 @@ distribute   := rtld-Rules \
+                  tst-tlsmod12.c tst-tls10.h tst-alignmod.c tst-alignmod2.c \
+                  circlemod1.c circlemod1a.c circlemod2.c circlemod2a.c \
+                  circlemod3.c circlemod3a.c nodlopenmod2.c \
++                 tst-tls19mod1.c tst-tls19mod2.c tst-tls19mod3.c \
+                  tls-macros.h \
+                  reldep8mod1.c reldep8mod2.c reldep8mod3.c \
+                  nodel2mod1.c nodel2mod2.c nodel2mod3.c \
+@@ -194,7 +195,7 @@ tests += loadtest restest1 preloadtest loadfail multiload origtest resolvfail \
+        restest2 next dblload dblunload reldep5 reldep6 reldep7 reldep8 \
+        circleload1 tst-tls3 tst-tls4 tst-tls5 tst-tls6 tst-tls7 tst-tls8 \
+        tst-tls10 tst-tls11 tst-tls12 tst-tls13 tst-tls14 tst-tls15 \
+-       tst-tls16 tst-tls17 tst-tls18 tst-tls-dlinfo \
++       tst-tls16 tst-tls17 tst-tls18 tst-tls19 tst-tls-dlinfo \
+        tst-align tst-align2 $(tests-execstack-$(have-z-execstack)) \
+        tst-dlmodcount tst-dlopenrpath tst-deep1 \
+        tst-dlmopen1 tst-dlmopen2 tst-dlmopen3 \
+@@ -240,6 +241,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
+               $(patsubst %,tst-tlsmod17a%,$(tlsmod17a-suffixes)) \
+               tst-tlsmod17b \
+               $(patsubst %,tst-tlsmod18a%,$(tlsmod18a-suffixes)) \
++              tst-tls19mod1 tst-tls19mod2 tst-tls19mod3 \
+               circlemod1 circlemod1a circlemod2 circlemod2a \
+               circlemod3 circlemod3a \
+               reldep8mod1 reldep8mod2 reldep8mod3 \
+@@ -525,6 +527,8 @@ $(objpfx)tst-tlsmod13a.so: $(objpfx)tst-tlsmod13.so
+ # For tst-tls9-static, make sure the modules it dlopens have libc.so in DT_NEEDED
+ $(objpfx)tst-tlsmod5.so: $(common-objpfx)libc.so
+ $(objpfx)tst-tlsmod6.so: $(common-objpfx)libc.so
++$(objpfx)tst-tls19mod1.so: $(objpfx)tst-tls19mod2.so $(objpfx)tst-tls19mod3.so
++$(objpfx)tst-tls19mod3.so: $(objpfx)ld.so
+ $(objpfx)reldep8mod3.so: $(objpfx)reldep8mod1.so $(objpfx)reldep8mod2.so
+ $(objpfx)nodel2mod3.so: $(objpfx)nodel2mod1.so $(objpfx)nodel2mod2.so
+ $(objpfx)reldep9mod2.so: $(objpfx)reldep9mod1.so
+@@ -822,6 +826,9 @@ $(patsubst %,$(objpfx)%.os,$(tlsmod18a-modules)): $(objpfx)tst-tlsmod18a%.os : t
+       $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ -DN=$* -DNOT_IN_libc=1 $<
+ $(patsubst %,$(objpfx)tst-tlsmod18a%.so,$(tlsmod18a-suffixes)): $(objpfx)tst-tlsmod18a%.so: $(objpfx)ld.so
++$(objpfx)tst-tls19: $(libdl)
++$(objpfx)tst-tls19.out: $(objpfx)tst-tls19mod1.so
++
+ CFLAGS-tst-align.c = $(stack-align-test-flags)
+ CFLAGS-tst-align2.c = $(stack-align-test-flags)
+ CFLAGS-tst-alignmod.c = $(stack-align-test-flags)
+diff --git a/elf/dl-open.c b/elf/dl-open.c
+index cf8e8cc..8d90b56 100644
+--- a/elf/dl-open.c
++++ b/elf/dl-open.c
+@@ -347,6 +347,7 @@ dl_open_worker (void *a)
+   /* If the file is not loaded now as a dependency, add the search
+      list of the newly loaded object to the scope.  */
+   bool any_tls = false;
++  unsigned int first_static_tls = new->l_searchlist.r_nlist;
+   for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i)
+     {
+       struct link_map *imap = new->l_searchlist.r_list[i];
+@@ -425,30 +426,9 @@ dl_open_worker (void *a)
+            might have to increase its size.  */
+         _dl_add_to_slotinfo (imap);
+-        if (imap->l_need_tls_init)
+-          {
+-            /* For static TLS we have to allocate the memory here
+-               and now.  This includes allocating memory in the DTV.
+-               But we cannot change any DTV other than our own. So,
+-               if we cannot guarantee that there is room in the DTV
+-               we don't even try it and fail the load.
+-
+-               XXX We could track the minimum DTV slots allocated in
+-               all threads.  */
+-            if (! RTLD_SINGLE_THREAD_P && imap->l_tls_modid > DTV_SURPLUS)
+-              _dl_signal_error (0, "dlopen", NULL, N_("\
+-cannot load any more object with static TLS"));
+-
+-            imap->l_need_tls_init = 0;
+-#ifdef SHARED
+-            /* Update the slot information data for at least the
+-               generation of the DSO we are allocating data for.  */
+-            _dl_update_slotinfo (imap->l_tls_modid);
+-#endif
+-
+-            GL(dl_init_static_tls) (imap);
+-            assert (imap->l_need_tls_init == 0);
+-          }
++        if (imap->l_need_tls_init
++            && first_static_tls == new->l_searchlist.r_nlist)
++          first_static_tls = i;
+         /* We have to bump the generation counter.  */
+         any_tls = true;
+@@ -460,6 +440,40 @@ cannot load any more object with static TLS"));
+     _dl_fatal_printf (N_("\
+ TLS generation counter wrapped!  Please report this."));
++  /* We need a second pass for static tls data, because _dl_update_slotinfo
++     must not be run while calls to _dl_add_to_slotinfo are still pending. */
++  for (unsigned int i = first_static_tls; i < new->l_searchlist.r_nlist; ++i)
++    {
++      struct link_map *imap = new->l_searchlist.r_list[i];
++
++      if (imap->l_need_tls_init
++        && ! imap->l_init_called
++        && imap->l_tls_blocksize > 0)
++      {
++        /* For static TLS we have to allocate the memory here and
++           now.  This includes allocating memory in the DTV.  But we
++           cannot change any DTV other than our own. So, if we
++           cannot guarantee that there is room in the DTV we don't
++           even try it and fail the load.
++
++           XXX We could track the minimum DTV slots allocated in
++           all threads.  */
++        if (! RTLD_SINGLE_THREAD_P && imap->l_tls_modid > DTV_SURPLUS)
++          _dl_signal_error (0, "dlopen", NULL, N_("\
++cannot load any more object with static TLS"));
++
++        imap->l_need_tls_init = 0;
++#ifdef SHARED
++        /* Update the slot information data for at least the
++           generation of the DSO we are allocating data for.  */
++        _dl_update_slotinfo (imap->l_tls_modid);
++#endif
++
++        GL(dl_init_static_tls) (imap);
++        assert (imap->l_need_tls_init == 0);
++      }
++    }
++
+   /* Notify the debugger all new objects have been relocated.  */
+   if (relocation_in_progress)
+     LIBC_PROBE (rtld_reloc_complete, 3, args->nsid, r, new);
+diff --git a/elf/tst-tls19.c b/elf/tst-tls19.c
+new file mode 100644
+index 0000000..acbc1d6
+--- /dev/null
++++ b/elf/tst-tls19.c
+@@ -0,0 +1,27 @@
++// BZ 12453
++#include <stdio.h>
++#include <dlfcn.h>
++
++
++static int
++do_test (void)
++{
++  void* dl = dlopen ("tst-tls19mod1.so", RTLD_LAZY | RTLD_GLOBAL);
++  if (dl == NULL)
++    {
++      printf ("Error loading tst-tls19mod1.so: %s\n", dlerror ());
++      return 1;
++    }
++
++  int (*fn) (void) = dlsym (dl, "foo");
++  if (fn == NULL)
++    {
++      printf("Error obtaining symbol foo\n");
++      return 1;
++    }
++
++  return fn ();
++}
++
++#define TEST_FUNCTION do_test ()
++#include "../test-skeleton.c"
+diff --git a/elf/tst-tls19mod1.c b/elf/tst-tls19mod1.c
+new file mode 100644
+index 0000000..2790097
+--- /dev/null
++++ b/elf/tst-tls19mod1.c
+@@ -0,0 +1,15 @@
++#include <stdio.h>
++
++extern int bar (void);
++extern int baz (void);
++
++int
++foo (void)
++{
++  int v1 = bar ();
++  int v2 = baz ();
++
++  printf ("bar=%d, baz=%d\n", v1, v2);
++
++  return v1 != 666 || v2 != 42;
++}
+diff --git a/elf/tst-tls19mod2.c b/elf/tst-tls19mod2.c
+new file mode 100644
+index 0000000..cae702f
+--- /dev/null
++++ b/elf/tst-tls19mod2.c
+@@ -0,0 +1,13 @@
++static int __thread tbar __attribute__ ((tls_model ("initial-exec"))) = 666;
++
++void
++setter (int a)
++{
++  tbar = a;
++}
++
++int
++bar (void)
++{
++  return tbar;
++}
+diff --git a/elf/tst-tls19mod3.c b/elf/tst-tls19mod3.c
+new file mode 100644
+index 0000000..e7b2801
+--- /dev/null
++++ b/elf/tst-tls19mod3.c
+@@ -0,0 +1,16 @@
++#include <stdio.h>
++
++static int __thread tbaz __attribute__ ((tls_model ("local-dynamic"))) = 42;
++
++void
++setter2 (int a)
++{
++  tbaz = a;
++}
++
++int
++baz (void)
++{
++  printf ("&tbaz=%p\n", &tbaz);
++  return tbaz;
++}