This is the mail archive of the glibc-cvs@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

GNU C Library master sources branch linaro/2.21/master updated. glibc-2.21-50-g1b24839


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GNU C Library master sources".

The branch, linaro/2.21/master has been updated
       via  1b248392fbadaa5a81ed220fdf2f7ec3b9939b9e (commit)
       via  cf161d39113047a81f969f277707b9d1679ecf69 (commit)
       via  a274119b0e6b4800163d8d1870995e597a55cb8b (commit)
       via  a18c25e0ee00bb37c724123d8b187050e9a367a0 (commit)
       via  124ea50584d3d21fe4c2fe9383508efd0ac9010a (commit)
       via  5a8692a64640bb648de6be9877dc072d728859bd (commit)
       via  6a277a415c127f6ee9a70548c899a7ac536f4288 (commit)
       via  9e1e8f68d59e33baa0eef9e8690d3cbaa5f67a24 (commit)
       via  dd08634b37f81f01441d842087b123d12b4003d4 (commit)
       via  749c94d44c9aa063dfc7ad2b505c8e6d35e57345 (commit)
       via  b533edfeedf66e971b1b29affd8e2f1619987dc9 (commit)
       via  4b31bd831ab205ff24ae3a2b8c4f8135c182c7d7 (commit)
       via  50992336ac40625c363e1514890f92774c65054a (commit)
       via  39ad1d12448adf0682ffb8b8ac146e99b68279bf (commit)
       via  7f2efd6c269a2ab2eee80bffa5bcb2538aa89641 (commit)
      from  a68cafa11c500d8a49a3014c43c5152859d037ae (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=1b248392fbadaa5a81ed220fdf2f7ec3b9939b9e

commit 1b248392fbadaa5a81ed220fdf2f7ec3b9939b9e
Author: H.J. Lu <hjl.tools@gmail.com>
Date:   Wed Jul 29 11:57:54 2015 -0700

    Extend local PLT reference check
    
    On x86, linker in binutils 2.26 and newer consolidates R_*_JUMP_SLOT with
    R_*_GLOB_DAT relocation against the same symbol.  This patch extends
    local PLT reference check to support alternate relocations.
    
    	[BZ #18078]
    	* scripts/check-localplt.awk: Support alternate relocations.
    	* scripts/localplt.awk: Also check relocations in DT_RELA/DT_REL
    	sections.
    	* sysdeps/unix/sysv/linux/i386/localplt.data: Mark free and
    	malloc entries with + REL R_386_GLOB_DAT.
    	* sysdeps/x86_64/localplt.data: New file.

diff --git a/ChangeLog b/ChangeLog
index b317543..907cc1d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2015-07-29  H.J. Lu  <hongjiu.lu@intel.com>
+
+	[BZ #18078]
+	* scripts/check-localplt.awk: Support alternate relocations.
+	* scripts/localplt.awk: Also check relocations in DT_RELA/DT_REL
+	sections.
+	* sysdeps/unix/sysv/linux/i386/localplt.data: Mark free and
+	malloc entries with + REL R_386_GLOB_DAT.
+	* sysdeps/x86_64/localplt.data: New file.
+
 2015-03-03  Alan Modra  <amodra@gmail.com>
 
 	[BZ #16512]
diff --git a/NEWS b/NEWS
index e0ed68d..f6c2650 100644
--- a/NEWS
+++ b/NEWS
@@ -10,7 +10,7 @@ Version 2.21.1
 * The following bugs are resolved with this release:
 
   16512, 17090, 17269, 17620, 17621, 17628, 17905, 17949, 18007, 18032,
-  18080, 18240, 18287, 18508, 18694, 18887, 18985, 19048, 19682.
+  18078, 18080, 18240, 18287, 18508, 18694, 18887, 18985, 19048, 19682.
 
 * A stack-based buffer overflow was found in libresolv when invoked from
   libnss_dns, allowing specially crafted DNS responses to seize control
diff --git a/scripts/check-localplt.awk b/scripts/check-localplt.awk
index bb1b912..3965292 100644
--- a/scripts/check-localplt.awk
+++ b/scripts/check-localplt.awk
@@ -3,9 +3,14 @@
 # Each line is either a comment starting with # or it looks like:
 #	libfoo.so: function
 # or
+#	libfoo.so: function + {RELA|REL} RELOC
+# or
 #	libfoo.so: function ?
-# The latter means that a PLT entry for function is optional in libfoo.so.
-# The former means one is required.
+# The first entry means that one is required.
+# The second entry means that one is required and relocation may also be
+# {RELA|REL} RELOC.
+# The third entry means that a PLT entry for function is optional in
+# libfoo.so.
 # The second file argument is - and this (stdin) receives the output
 # of the check-localplt program.
 
@@ -14,7 +19,10 @@ BEGIN { result = 0 }
 FILENAME != "-" && /^#/ { next }
 
 FILENAME != "-" {
-  if (NF != 2 && !(NF == 3 && $3 == "?")) {
+  if (NF == 5 && $3 == "+" && ($4 == "RELA" || $4 == "REL")) {
+    accept_type[$1 " " $2] = $4;
+    accept_reloc[$1 " " $2] = $5;
+  } else if (NF != 2 && !(NF == 3 && $3 == "?")) {
     printf "%s:%d: bad data line: %s\n", FILENAME, FNR, $0 > "/dev/stderr";
     result = 2;
   } else {
@@ -23,7 +31,7 @@ FILENAME != "-" {
   next;
 }
 
-NF != 2 {
+NF != 2 && !(NF == 4 && ($3 == "RELA" || $3 == "REL")) {
   print "Unexpected output from check-localplt:", $0 > "/dev/stderr";
   result = 2;
   next
@@ -31,7 +39,23 @@ NF != 2 {
 
 {
   key = $1 " " $2
-  if (key in accept) {
+  if ($3 == "RELA" || $3 == "REL") {
+    # Entries like:
+    # libc.so: free + RELA R_X86_64_GLOB_DAT
+    # may be ignored.
+    if (key in accept_type && accept_type[key] == $3 && accept_reloc[key] == $4) {
+      # Match
+      # libc.so: free + RELA R_X86_64_GLOB_DAT
+      delete accept_type[key]
+    }
+  } else if (NF == 2 && key in accept_reloc) {
+    # Match
+    # libc.so: free
+    # against
+    # libc.so: free + RELA R_X86_64_GLOB_DAT
+    if (key in accept_type)
+      delete accept_type[key]
+  } else if (key in accept) {
     delete accept[key]
   } else {
     print "Extra PLT reference:", $0;
@@ -49,5 +73,11 @@ END {
     }
   }
 
+  for (key in accept_type) {
+    # It's mandatory.
+    print "Missing required PLT or " accept_reloc[key] " reference:", key;
+    result = 1;
+  }
+
   exit(result);
 }
diff --git a/scripts/localplt.awk b/scripts/localplt.awk
index 84c94d1..f75b3b4 100644
--- a/scripts/localplt.awk
+++ b/scripts/localplt.awk
@@ -13,6 +13,8 @@ FILENAME != lastfile {
   }
   lastfile = FILENAME;
   jmprel_offset = 0;
+  rela_offset = 0;
+  rel_offset = 0;
   delete section_offset_by_address;
 }
 
@@ -43,6 +45,30 @@ in_relocs && relocs_offset == jmprel_offset && NF >= 5 {
   }
 }
 
+in_relocs && relocs_offset == rela_offset && NF >= 5 {
+  # Relocations against GNU_IFUNC symbols are not shown as an hexadecimal
+  # value, but rather as the resolver symbol followed by ().
+  if ($4 ~ /\(\)/) {
+    print whatfile, gensub(/@.*/, "", "g", $5), "RELA", $3
+  } else {
+    symval = strtonum("0x" $4);
+    if (symval != 0)
+      print whatfile, gensub(/@.*/, "", "g", $5), "RELA", $3
+  }
+}
+
+in_relocs && relocs_offset == rel_offset && NF >= 5 {
+  # Relocations against GNU_IFUNC symbols are not shown as an hexadecimal
+  # value, but rather as the resolver symbol followed by ().
+  if ($4 ~ /\(\)/) {
+    print whatfile, gensub(/@.*/, "", "g", $5), "REL", $3
+  } else {
+    symval = strtonum("0x" $4);
+    if (symval != 0)
+      print whatfile, gensub(/@.*/, "", "g", $5), "REL", $3
+  }
+}
+
 in_relocs { next }
 
 $1 == "Relocation" && $2 == "section" && $5 == "offset" {
@@ -62,4 +88,25 @@ $2 == "(JMPREL)" {
   next
 }
 
+$2 == "(RELA)" {
+  rela_addr = strtonum($3);
+  if (rela_addr in section_offset_by_address) {
+    rela_offset = section_offset_by_address[rela_addr];
+  } else {
+    print FILENAME ": *** DT_RELA does not match any section's address";
+    result = 2;
+  }
+  next
+}
+
+$2 == "(REL)" {
+  rel_addr = strtonum($3);
+  if (rel_addr in section_offset_by_address) {
+    rel_offset = section_offset_by_address[rel_addr];
+  } else {
+    print FILENAME ": *** DT_REL does not match any section's address";
+    result = 2;
+  }
+  next
+}
 END { exit(result) }
diff --git a/sysdeps/unix/sysv/linux/i386/localplt.data b/sysdeps/unix/sysv/linux/i386/localplt.data
index b25abf8..2e03821 100644
--- a/sysdeps/unix/sysv/linux/i386/localplt.data
+++ b/sysdeps/unix/sysv/linux/i386/localplt.data
@@ -1,7 +1,9 @@
+# Linker in binutils 2.26 and newer consolidates R_X86_64_JUMP_SLOT
+# relocation with R_386_GLOB_DAT relocation against the same symbol.
 libc.so: _Unwind_Find_FDE
 libc.so: calloc
-libc.so: free
-libc.so: malloc
+libc.so: free + REL R_386_GLOB_DAT
+libc.so: malloc + REL R_386_GLOB_DAT
 libc.so: memalign
 libc.so: realloc
 libm.so: matherr
@@ -12,4 +14,4 @@ ld.so: __libc_memalign
 ld.so: malloc
 ld.so: calloc
 ld.so: realloc
-ld.so: free
+ld.so: free + REL R_386_GLOB_DAT
diff --git a/sysdeps/x86_64/localplt.data b/sysdeps/x86_64/localplt.data
new file mode 100644
index 0000000..d140476
--- /dev/null
+++ b/sysdeps/x86_64/localplt.data
@@ -0,0 +1,19 @@
+# See scripts/check-localplt.awk for how this file is processed.
+# PLT use is required for the malloc family and for matherr because
+# users can define their own functions and have library internals call them.
+# Linker in binutils 2.26 and newer consolidates R_X86_64_JUMP_SLOT
+# relocation with R_X86_64_GLOB_DAT relocation against the same symbol.
+libc.so: calloc
+libc.so: free + RELA R_X86_64_GLOB_DAT
+libc.so: malloc + RELA R_X86_64_GLOB_DAT
+libc.so: memalign
+libc.so: realloc
+libm.so: matherr
+# The dynamic loader uses __libc_memalign internally to allocate aligned
+# TLS storage. The other malloc family of functions are expected to allow
+# user symbol interposition.
+ld.so: __libc_memalign
+ld.so: malloc
+ld.so: calloc
+ld.so: realloc
+ld.so: free + RELA R_X86_64_GLOB_DAT

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=cf161d39113047a81f969f277707b9d1679ecf69

commit cf161d39113047a81f969f277707b9d1679ecf69
Author: Alan Modra <amodra@gmail.com>
Date:   Fri Feb 20 15:23:28 2015 +1030

    Fix localplt test breakage with new readelf
    
    Since 2014-11-24 binutils git commit bb4d2ac2, readelf has appended
    the symbol version to symbols shown in reloc dumps.
    
    	[BZ #16512]
    	* scripts/localplt.awk: Strip off symbol version.
    	* NEWS: Mention bug fix.

diff --git a/ChangeLog b/ChangeLog
index 54ff9f8..b317543 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2015-03-03  Alan Modra  <amodra@gmail.com>
+
+	[BZ #16512]
+	* scripts/localplt.awk: Strip off symbol version.
+	* NEWS: Mention bug fix.
+
 2016-08-26  Florian Weimer  <fweimer@redhat.com>
 
 	[BZ #20432]
diff --git a/NEWS b/NEWS
index f6f8685..e0ed68d 100644
--- a/NEWS
+++ b/NEWS
@@ -9,8 +9,8 @@ Version 2.21.1
 
 * The following bugs are resolved with this release:
 
-  17090, 17269, 17620, 17621, 17628, 17905, 17949, 18007, 18032, 18080,
-  18240, 18287, 18508, 18694, 18887, 18985, 19048, 19682.
+  16512, 17090, 17269, 17620, 17621, 17628, 17905, 17949, 18007, 18032,
+  18080, 18240, 18287, 18508, 18694, 18887, 18985, 19048, 19682.
 
 * A stack-based buffer overflow was found in libresolv when invoked from
   libnss_dns, allowing specially crafted DNS responses to seize control
diff --git a/scripts/localplt.awk b/scripts/localplt.awk
index f55c41a..84c94d1 100644
--- a/scripts/localplt.awk
+++ b/scripts/localplt.awk
@@ -35,11 +35,11 @@ in_relocs && relocs_offset == jmprel_offset && NF >= 5 {
   # Relocations against GNU_IFUNC symbols are not shown as an hexadecimal
   # value, but rather as the resolver symbol followed by ().
   if ($4 ~ /\(\)/) {
-    print whatfile, $5
+    print whatfile, gensub(/@.*/, "", "g", $5)
   } else {
     symval = strtonum("0x" $4);
     if (symval != 0)
-      print whatfile, $5
+      print whatfile, gensub(/@.*/, "", "g", $5)
   }
 }
 

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=a274119b0e6b4800163d8d1870995e597a55cb8b

commit a274119b0e6b4800163d8d1870995e597a55cb8b
Author: Florian Weimer <fweimer@redhat.com>
Date:   Fri Aug 26 22:40:27 2016 +0200

    malloc: Simplify static malloc interposition [BZ #20432]
    
    Existing interposed mallocs do not define the glibc-internal
    fork callbacks (and they should not), so statically interposed
    mallocs lead to link failures because the strong reference from
    fork pulls in glibc's malloc, resulting in multiple definitions
    of malloc-related symbols.

diff --git a/ChangeLog b/ChangeLog
index c1a6363..54ff9f8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,37 @@
+2016-08-26  Florian Weimer  <fweimer@redhat.com>
+
+	[BZ #20432]
+	Avoid strong references to malloc-internal symbols when linking
+	statically, to support statically interposed mallocs.
+	* include/libc-symbols.h (call_function_static_weak): New macro.
+	* malloc/Makefile (tests): Add tst-interpose-nothread,
+	tst-interpose-thread, tst-interpose-static-nothread,
+	tst-interpose-static-thread.
+	(tests-static): Add tst-interpose-static-nothread,
+	tst-interpose-static-thread.
+	(extra-tests-objs): Add tst-interpose-aux-nothread.o,
+	tst-interpose-aux-thread.o.
+	(test-extras): Add tst-interpose-aux-nothread,
+	tst-interpose-aux-thread.
+	(tst-interpose-nothread, tst-interpose-static-nothread): Link with
+	tst-interpose-aux-nothread.o.
+	(tst-interpose-thread, tst-interpose-static-thread): Link with
+	tst-interpose-aux-thread.o and libthread.
+	* malloc/tst-interpose-aux-nothread.c: New file.
+	* malloc/tst-interpose-aux-thread.c: Likewise.
+	* malloc/tst-interpose-aux.c: Likewise.
+	* malloc/tst-interpose-aux.h: Likewise.
+	* malloc/tst-interpose-nothread.c: Likewise.
+	* malloc/tst-interpose-skeleton.c: Likewise.
+	* malloc/tst-interpose-static-nothread.c: Likewise.
+	* malloc/tst-interpose-static-thread.c: Likewise.
+	* malloc/tst-interpose-thread.c: Likewise.
+	* nptl/tst-tls3-malloc.c: Use new interposed malloc.
+	* sysdeps/mach/hurd/fork.c (__fork): Only call
+	__malloc_fork_lock_parent, __malloc_fork_unlock_parent,
+	__malloc_fork_unlock_child if defined.
+	* sysdeps/nptl/fork.c (__libc_fork): Likewise.
+
 2016-08-16  Florian Weimer  <fweimer@redhat.com>
 
 	* nptl/tst-tls3.c (default_stack_size_in_mb, stack_size_in_mb):
diff --git a/include/libc-symbols.h b/include/libc-symbols.h
index 2da0ab4..bbe6619 100644
--- a/include/libc-symbols.h
+++ b/include/libc-symbols.h
@@ -138,6 +138,21 @@
 # define weak_extern(symbol) _weak_extern (weak symbol)
 # define _weak_extern(expr) _Pragma (#expr)
 
+/* In shared builds, the expression call_function_static_weak
+   (FUNCTION-SYMBOL, ARGUMENTS) invokes FUNCTION-SYMBOL (an
+   identifier) unconditionally, with the (potentially empty) argument
+   list ARGUMENTS.  In static builds, if FUNCTION-SYMBOL has a
+   definition, the function is invoked as before; if FUNCTION-SYMBOL
+   is NULL, no call is performed.  */
+# ifdef SHARED
+#  define call_function_static_weak(func, ...) func (__VA_ARGS__)
+# else	/* !SHARED */
+#  define call_function_static_weak(func, ...)		\
+  ({							\
+    extern __typeof__ (func) func weak_function;	\
+    (func != NULL ? func (__VA_ARGS__) : (void)0);	\
+  })
+# endif
 
 #else /* __ASSEMBLER__ */
 
diff --git a/malloc/Makefile b/malloc/Makefile
index 6e19a42..40f7215 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -27,7 +27,16 @@ headers := $(dist-headers) obstack.h mcheck.h
 tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
 	 tst-mallocstate tst-mcheck tst-mallocfork tst-trim1 \
 	 tst-malloc-usable tst-realloc tst-posix_memalign \
-	 tst-pvalloc tst-memalign tst-mallopt tst-malloc-fork-deadlock
+	 tst-pvalloc tst-memalign tst-mallopt tst-malloc-fork-deadlock \
+	 tst-interpose-nothread \
+	 tst-interpose-thread \
+	 tst-interpose-static-nothread \
+	 tst-interpose-static-thread \
+
+tests-static := \
+	 tst-interpose-static-nothread \
+	 tst-interpose-static-thread \
+
 test-srcs = tst-mtrace
 
 routines = malloc morecore mcheck mtrace obstack
@@ -39,6 +48,15 @@ non-lib.a := libmcheck.a
 extra-libs = libmemusage
 extra-libs-others = $(extra-libs)
 
+# Helper objects for some tests.
+extra-tests-objs += \
+  tst-interpose-aux-nothread.o \
+  tst-interpose-aux-thread.o \
+
+test-extras = \
+  tst-interpose-aux-nothread \
+  tst-interpose-aux-thread \
+
 libmemusage-routines = memusage
 libmemusage-inhibit-o = $(filter-out .os,$(object-suffixes))
 
@@ -162,3 +180,10 @@ $(foreach o,$(all-object-suffixes),$(objpfx)malloc$(o)): arena.c hooks.c
 # Compile the tests with a flag which suppresses the mallopt call in
 # the test skeleton.
 $(tests:%=$(objpfx)%.o): CPPFLAGS += -DTEST_NO_MALLOPT
+
+$(objpfx)tst-interpose-nothread: $(objpfx)tst-interpose-aux-nothread.o
+$(objpfx)tst-interpose-thread: \
+  $(objpfx)tst-interpose-aux-thread.o $(shared-thread-library)
+$(objpfx)tst-interpose-static-nothread: $(objpfx)tst-interpose-aux-nothread.o
+$(objpfx)tst-interpose-static-thread: \
+  $(objpfx)tst-interpose-aux-thread.o $(static-thread-library)
diff --git a/malloc/tst-interpose-aux-nothread.c b/malloc/tst-interpose-aux-nothread.c
new file mode 100644
index 0000000..0eae66f
--- /dev/null
+++ b/malloc/tst-interpose-aux-nothread.c
@@ -0,0 +1,20 @@
+/* Interposed malloc, version without threading support.
+   Copyright (C) 2016 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; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#define INTERPOSE_THREADS 0
+#include "tst-interpose-aux.c"
diff --git a/malloc/tst-interpose-aux-thread.c b/malloc/tst-interpose-aux-thread.c
new file mode 100644
index 0000000..354e4d8
--- /dev/null
+++ b/malloc/tst-interpose-aux-thread.c
@@ -0,0 +1,20 @@
+/* Interposed malloc, version with threading support.
+   Copyright (C) 2016 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; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#define INTERPOSE_THREADS 1
+#include "tst-interpose-aux.c"
diff --git a/malloc/tst-interpose-aux.c b/malloc/tst-interpose-aux.c
new file mode 100644
index 0000000..6092e98
--- /dev/null
+++ b/malloc/tst-interpose-aux.c
@@ -0,0 +1,270 @@
+/* Minimal malloc implementation for interposition tests.
+   Copyright (C) 2016 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; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#include "tst-interpose-aux.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#if INTERPOSE_THREADS
+#include <pthread.h>
+#endif
+
+/* Print the error message and terminate the process with status 1.  */
+__attribute__ ((noreturn))
+__attribute__ ((format (printf, 1, 2)))
+static void *
+fail (const char *format, ...)
+{
+  /* This assumes that vsnprintf will not call malloc.  It does not do
+     so for the format strings we use.  */
+  char message[4096];
+  va_list ap;
+  va_start (ap, format);
+  vsnprintf (message, sizeof (message), format, ap);
+  va_end (ap);
+
+  enum { count = 3 };
+  struct iovec iov[count];
+
+  iov[0].iov_base = (char *) "error: ";
+  iov[1].iov_base = (char *) message;
+  iov[2].iov_base = (char *) "\n";
+
+  for (int i = 0; i < count; ++i)
+    iov[i].iov_len = strlen (iov[i].iov_base);
+
+  int unused __attribute__ ((unused));
+  unused = writev (STDOUT_FILENO, iov, count);
+  _exit (1);
+}
+
+#if INTERPOSE_THREADS
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+static void
+lock (void)
+{
+#if INTERPOSE_THREADS
+  int ret = pthread_mutex_lock (&mutex);
+  if (ret != 0)
+    {
+      errno = ret;
+      fail ("pthread_mutex_lock: %m");
+    }
+#endif
+}
+
+static void
+unlock (void)
+{
+#if INTERPOSE_THREADS
+  int ret = pthread_mutex_unlock (&mutex);
+  if (ret != 0)
+    {
+      errno = ret;
+      fail ("pthread_mutex_unlock: %m");
+    }
+#endif
+}
+
+struct __attribute__ ((aligned (__alignof__ (long double)))) allocation_header
+{
+  size_t allocation_index;
+  size_t allocation_size;
+};
+
+/* Array of known allocations, to track invalid frees.  */
+enum { max_allocations = 65536 };
+static struct allocation_header *allocations[max_allocations];
+static size_t allocation_index;
+static size_t deallocation_count;
+
+/* Sanity check for successful malloc interposition.  */
+__attribute__ ((destructor))
+static void
+check_for_allocations (void)
+{
+  if (allocation_index == 0)
+    {
+      /* Make sure that malloc is called at least once from libc.  */
+      void *volatile ptr = strdup ("ptr");
+      free (ptr);
+      /* Compiler barrier.  The strdup function calls malloc, which
+         updates allocation_index, but strdup is marked __THROW, so
+         the compiler could optimize away the reload.  */
+      __asm__ volatile ("" ::: "memory");
+      /* If the allocation count is still zero, it means we did not
+         interpose malloc successfully.  */
+      if (allocation_index == 0)
+        fail ("malloc does not seem to have been interposed");
+    }
+}
+
+static struct allocation_header *get_header (const char *op, void *ptr)
+{
+  struct allocation_header *header = ((struct allocation_header *) ptr) - 1;
+  if (header->allocation_index >= allocation_index)
+    fail ("%s: %p: invalid allocation index: %zu (not less than %zu)",
+          op, ptr, header->allocation_index, allocation_index);
+  if (allocations[header->allocation_index] != header)
+    fail ("%s: %p: allocation pointer does not point to header, but %p",
+          op, ptr, allocations[header->allocation_index]);
+  return header;
+}
+
+/* Internal helper functions.  Those must be called while the lock is
+   acquired.  */
+
+static void *
+malloc_internal (size_t size)
+{
+  if (allocation_index == max_allocations)
+    {
+      errno = ENOMEM;
+      return NULL;
+    }
+  size_t allocation_size = size + sizeof (struct allocation_header);
+  if (allocation_size < size)
+    {
+      errno = ENOMEM;
+      return NULL;
+    }
+
+  size_t index = allocation_index++;
+  void *result = mmap (NULL, allocation_size, PROT_READ | PROT_WRITE,
+                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+  if (result == MAP_FAILED)
+    return NULL;
+  allocations[index] = result;
+  *allocations[index] = (struct allocation_header)
+    {
+      .allocation_index = index,
+      .allocation_size = allocation_size
+    };
+  return allocations[index] + 1;
+}
+
+static void
+free_internal (const char *op, struct allocation_header *header)
+{
+  size_t index = header->allocation_index;
+  int result = mprotect (header, header->allocation_size, PROT_NONE);
+  if (result != 0)
+    fail ("%s: mprotect (%p, %zu): %m", op, header, header->allocation_size);
+  /* Catch double-free issues.  */
+  allocations[index] = NULL;
+  ++deallocation_count;
+}
+
+static void *
+realloc_internal (void *ptr, size_t new_size)
+{
+  struct allocation_header *header = get_header ("realloc", ptr);
+  size_t old_size = header->allocation_size - sizeof (struct allocation_header);
+  if (old_size >= new_size)
+    return ptr;
+
+  void *newptr = malloc_internal (new_size);
+  if (newptr == NULL)
+    return NULL;
+  memcpy (newptr, ptr, old_size);
+  free_internal ("realloc", header);
+  return newptr;
+}
+
+/* Public interfaces.  These functions must perform locking.  */
+
+size_t
+malloc_allocation_count (void)
+{
+  lock ();
+  size_t count = allocation_index;
+  unlock ();
+  return count;
+}
+
+size_t
+malloc_deallocation_count (void)
+{
+  lock ();
+  size_t count = deallocation_count;
+  unlock ();
+  return count;
+}
+void *
+malloc (size_t size)
+{
+  lock ();
+  void *result = malloc_internal (size);
+  unlock ();
+  return result;
+}
+
+void
+free (void *ptr)
+{
+  if (ptr == NULL)
+    return;
+  lock ();
+  struct allocation_header *header = get_header ("free", ptr);
+  free_internal ("free", header);
+  unlock ();
+}
+
+void *
+calloc (size_t a, size_t b)
+{
+  if (b > 0 && a > SIZE_MAX / b)
+    {
+      errno = ENOMEM;
+      return NULL;
+    }
+  lock ();
+  /* malloc_internal uses mmap, so the memory is zeroed.  */
+  void *result = malloc_internal (a * b);
+  unlock ();
+  return result;
+}
+
+void *
+realloc (void *ptr, size_t n)
+{
+  if (n ==0)
+    {
+      free (ptr);
+      return NULL;
+    }
+  else if (ptr == NULL)
+    return malloc (n);
+  else
+    {
+      lock ();
+      void *result = realloc_internal (ptr, n);
+      unlock ();
+      return result;
+    }
+}
diff --git a/malloc/tst-interpose-aux.h b/malloc/tst-interpose-aux.h
new file mode 100644
index 0000000..2fb22d3
--- /dev/null
+++ b/malloc/tst-interpose-aux.h
@@ -0,0 +1,30 @@
+/* Statistics interface for the minimal malloc implementation.
+   Copyright (C) 2016 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; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef TST_INTERPOSE_AUX_H
+#define TST_INTERPOSE_AUX_H
+
+#include <stddef.h>
+
+/* Return the number of allocations performed.  */
+size_t malloc_allocation_count (void);
+
+/* Return the number of deallocations performed.  */
+size_t malloc_deallocation_count (void);
+
+#endif /* TST_INTERPOSE_AUX_H */
diff --git a/malloc/tst-interpose-nothread.c b/malloc/tst-interpose-nothread.c
new file mode 100644
index 0000000..9acb572
--- /dev/null
+++ b/malloc/tst-interpose-nothread.c
@@ -0,0 +1,20 @@
+/* Malloc interposition test, dynamically-linked version without threads.
+   Copyright (C) 2016 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; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#define INTERPOSE_THREADS 0
+#include "tst-interpose-skeleton.c"
diff --git a/malloc/tst-interpose-skeleton.c b/malloc/tst-interpose-skeleton.c
new file mode 100644
index 0000000..fa39e8b
--- /dev/null
+++ b/malloc/tst-interpose-skeleton.c
@@ -0,0 +1,210 @@
+/* Test driver for malloc interposition tests.
+   Copyright (C) 2016 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; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#if INTERPOSE_THREADS
+#include <pthread.h>
+#endif
+
+static int do_test (void);
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
+
+/* Fills BUFFER with a test string.  */
+static void
+line_string (int number, char *buffer, size_t length)
+{
+  for (size_t i = 0; i < length - 2; ++i)
+    buffer[i] = 'A' + ((number + i) % 26);
+  buffer[length - 2] = '\n';
+  buffer[length - 1] = '\0';
+}
+
+/* Perform the tests.  */
+static void *
+run_tests (void *closure)
+{
+  char *temp_file_path;
+  int fd = create_temp_file ("tst-malloc-interpose", &temp_file_path);
+  if (fd < 0)
+    _exit (1);
+
+  /* Line lengths excluding the line terminator.  */
+  static const int line_lengths[] = { 0, 45, 80, 2, 8201, 0, 17, -1 };
+
+  /* Fill the test file with data.  */
+  {
+    FILE *fp = fdopen (fd, "w");
+    for (int lineno = 0; line_lengths[lineno] >= 0; ++lineno)
+      {
+        char buffer[line_lengths[lineno] + 2];
+        line_string (lineno, buffer, sizeof (buffer));
+        fprintf (fp, "%s", buffer);
+      }
+
+    if (ferror (fp))
+      {
+        printf ("error: fprintf: %m\n");
+        _exit (1);
+      }
+    if (fclose (fp) != 0)
+      {
+        printf ("error: fclose: %m\n");
+        _exit (1);
+      }
+  }
+
+  /* Read the test file.  This tests libc-internal allocation with
+     realloc.  */
+  {
+    FILE *fp = fopen (temp_file_path, "r");
+
+    char *actual = NULL;
+    size_t actual_size = 0;
+    for (int lineno = 0; ; ++lineno)
+      {
+        errno = 0;
+        ssize_t result = getline (&actual, &actual_size, fp);
+        if (result == 0)
+          {
+            printf ("error: invalid return value 0 from getline\n");
+            _exit (1);
+          }
+        if (result < 0 && errno != 0)
+          {
+            printf ("error: getline: %m\n");
+            _exit (1);
+          }
+        if (result < 0 && line_lengths[lineno] >= 0)
+          {
+            printf ("error: unexpected end of file after line %d\n", lineno);
+            _exit (1);
+          }
+        if (result > 0 && line_lengths[lineno] < 0)
+          {
+            printf ("error: no end of file after line %d\n", lineno);
+            _exit (1);
+          }
+        if (result == -1 && line_lengths[lineno] == -1)
+          /* End of file reached as expected.  */
+          break;
+
+        if (result != line_lengths[lineno] + 1)
+          {
+            printf ("error: line length mismatch: expected %d, got %zd\n",
+                    line_lengths[lineno], result);
+            _exit (1);
+          }
+
+        char expected[line_lengths[lineno] + 2];
+        line_string (lineno, expected, sizeof (expected));
+        if (strcmp (actual, expected) != 0)
+          {
+            printf ("error: line mismatch\n");
+            printf ("error:   expected: [[%s]]\n", expected);
+            printf ("error:   actual:   [[%s]]\n", actual);
+            _exit (1);
+          }
+      }
+
+    if (fclose (fp) != 0)
+      {
+        printf ("error: fclose (after reading): %m\n");
+        _exit (1);
+      }
+  }
+
+  free (temp_file_path);
+
+  /* Make sure that fork is working.  */
+  pid_t pid = fork ();
+  if (pid == -1)
+    {
+      printf ("error: fork: %m\n");
+      _exit (1);
+    }
+  enum { exit_code = 55 };
+  if (pid == 0)
+    _exit (exit_code);
+  int status;
+  int ret = waitpid (pid, &status, 0);
+  if (ret < 0)
+    {
+      printf ("error: waitpid: %m\n");
+      _exit (1);
+    }
+  if (!WIFEXITED (status) || WEXITSTATUS (status) != exit_code)
+    {
+      printf ("error: unexpected exit status from child process: %d\n",
+              status);
+      _exit (1);
+    }
+
+  return NULL;
+}
+
+/* This is used to detect if malloc has not been successfully
+   interposed.  The interposed malloc does not use brk/sbrk.  */
+static void *initial_brk;
+__attribute__ ((constructor))
+static void
+set_initial_brk (void)
+{
+  initial_brk = sbrk (0);
+}
+
+/* Terminate the process if the break value has been changed.  */
+__attribute__ ((destructor))
+static void
+check_brk (void)
+{
+  void *current = sbrk (0);
+  if (current != initial_brk)
+    {
+      printf ("error: brk changed from %p to %p; no interposition?\n",
+              initial_brk, current);
+      _exit (1);
+    }
+}
+
+static int
+do_test (void)
+{
+  check_brk ();
+
+#if INTERPOSE_THREADS
+  pthread_t thr;
+  int ret;
+  if ((ret = pthread_create (&thr, NULL, run_tests, NULL)) != 0)
+    {
+      errno = ret;
+      printf ("error: %s failed: %m\n", __func__);
+      exit (EXIT_FAILURE);
+    }
+#else
+  run_tests (NULL);
+#endif
+
+  check_brk ();
+
+  return 0;
+}
diff --git a/malloc/tst-interpose-static-nothread.c b/malloc/tst-interpose-static-nothread.c
new file mode 100644
index 0000000..3fb2dd8
--- /dev/null
+++ b/malloc/tst-interpose-static-nothread.c
@@ -0,0 +1,19 @@
+/* Malloc interposition test, static version without threads.
+   Copyright (C) 2016 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; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#include "tst-interpose-nothread.c"
diff --git a/malloc/tst-interpose-static-thread.c b/malloc/tst-interpose-static-thread.c
new file mode 100644
index 0000000..c78ebc7
--- /dev/null
+++ b/malloc/tst-interpose-static-thread.c
@@ -0,0 +1,19 @@
+/* Malloc interposition test, static version with threads.
+   Copyright (C) 2016 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; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#include "tst-interpose-nothread.c"
diff --git a/malloc/tst-interpose-thread.c b/malloc/tst-interpose-thread.c
new file mode 100644
index 0000000..d3e20c7
--- /dev/null
+++ b/malloc/tst-interpose-thread.c
@@ -0,0 +1,20 @@
+/* Malloc interposition test, dynamically-linked version with threads.
+   Copyright (C) 2016 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; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#define INTERPOSE_THREADS 1
+#include "tst-interpose-skeleton.c"
diff --git a/nptl/tst-tls3-malloc.c b/nptl/tst-tls3-malloc.c
index 8a580fa..719ab28 100644
--- a/nptl/tst-tls3-malloc.c
+++ b/nptl/tst-tls3-malloc.c
@@ -17,6 +17,7 @@
    <http://www.gnu.org/licenses/>.  */
 
 /* Reuse the test.  */
+#define STACK_SIZE_MB 5
 #include "tst-tls3.c"
 
 /* Increase the thread stack size to 10 MiB, so that some thread
@@ -26,156 +27,5 @@ static long stack_size_in_mb = 10;
 
 #include <sys/mman.h>
 
-/* Interpose a minimal malloc implementation.  This implementation
-   deliberately interposes just a restricted set of symbols, to detect
-   if the TLS code bypasses the interposed malloc.  */
-
-/* Lock to guard malloc internals.  */
-static pthread_mutex_t malloc_lock = PTHREAD_MUTEX_INITIALIZER;
-
-/* Information about an allocation chunk.  */
-struct malloc_chunk
-{
-  /* Start of the allocation.  */
-  void *start;
-  /* Size of the allocation.  */
-  size_t size;
-};
-
-enum { malloc_chunk_count = 1000 };
-static struct malloc_chunk chunks[malloc_chunk_count];
-
-/* Lock the malloc lock.  */
-static void
-xlock (void)
-{
-  int ret = pthread_mutex_lock (&malloc_lock);
-  if (ret != 0)
-    {
-      errno = ret;
-      printf ("error: pthread_mutex_lock: %m\n");
-      _exit (1);
-    }
-}
-
-/* Unlock the malloc lock.  */
-static void
-xunlock (void)
-{
-  int ret = pthread_mutex_unlock (&malloc_lock);
-  if (ret != 0)
-    {
-      errno = ret;
-      printf ("error: pthread_mutex_unlock: %m\n");
-      _exit (1);
-    }
-}
-
-/* Internal malloc without locking and registration.  */
-static void *
-malloc_internal (size_t size)
-{
-  void *result = mmap (NULL, size, PROT_READ | PROT_WRITE,
-                       MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
-  if (result == MAP_FAILED)
-    {
-      printf ("error: mmap: %m\n");
-      _exit (1);
-    }
-  return result;
-}
-
-void *
-malloc (size_t size)
-{
-  if (size == 0)
-    size = 1;
-  xlock ();
-  void *result = malloc_internal (size);
-  for (int i = 0; i < malloc_chunk_count; ++i)
-    if (chunks[i].start == NULL)
-      {
-        chunks[i].start = result;
-        chunks[i].size = size;
-        xunlock ();
-        return result;
-      }
-  xunlock ();
-  printf ("error: no place to store chunk pointer\n");
-  _exit (1);
-}
-
-void *
-calloc (size_t a, size_t b)
-{
-  if (b != 0 && a > SIZE_MAX / b)
-    return NULL;
-  /* malloc uses mmap, which provides zeroed memory.  */
-  return malloc (a * b);
-}
-
-static void
-xunmap (void *ptr, size_t size)
-{
-  int ret = munmap (ptr, size);
-  if (ret < 0)
-    {
-      printf ("error: munmap (%p, %zu) failed: %m\n", ptr, size);
-      _exit (1);
-    }
-}
-
-void
-free (void *ptr)
-{
-  if (ptr == NULL)
-    return;
-
-  xlock ();
-  for (int i = 0; i < malloc_chunk_count; ++i)
-    if (chunks[i].start == ptr)
-      {
-        xunmap (ptr, chunks[i].size);
-        chunks[i] = (struct malloc_chunk) {};
-        xunlock ();
-        return;
-      }
-  xunlock ();
-  printf ("error: tried to free non-allocated pointer %p\n", ptr);
-  _exit (1);
-}
-
-void *
-realloc (void *old, size_t size)
-{
-  if (old != NULL)
-    {
-      xlock ();
-      for (int i = 0; i < malloc_chunk_count; ++i)
-        if (chunks[i].start == old)
-          {
-            size_t old_size = chunks[i].size;
-            void *result;
-            if (old_size < size)
-              {
-                result = malloc_internal (size);
-                /* Reuse the slot for the new allocation.  */
-                memcpy (result, old, old_size);
-                xunmap (old, old_size);
-                chunks[i].start = result;
-                chunks[i].size = size;
-              }
-            else
-              /* Old size is not smaller, so reuse the old
-                 allocation.  */
-              result = old;
-            xunlock ();
-            return result;
-          }
-      xunlock ();
-      printf ("error: tried to realloc non-allocated pointer %p\n", old);
-      _exit (1);
-    }
-  else
-    return malloc (size);
-}
+#define INTERPOSE_THREADS 1
+#include "../malloc/tst-interpose-aux.c"
diff --git a/sysdeps/mach/hurd/fork.c b/sysdeps/mach/hurd/fork.c
index 734c593..4668383 100644
--- a/sysdeps/mach/hurd/fork.c
+++ b/sysdeps/mach/hurd/fork.c
@@ -112,7 +112,7 @@ __fork (void)
 	 handlers may use malloc, and the libio list lock has an
 	 indirect malloc dependency as well (via the getdelim
 	 function).  */
-      __malloc_fork_lock_parent ();
+      call_function_static_weak (__malloc_fork_lock_parent);
 
       /* Lock things that want to be locked before we fork.  */
       {
@@ -616,7 +616,7 @@ __fork (void)
 	}
 
       /* Release malloc locks.  */
-      __malloc_fork_unlock_parent ();
+      call_function_static_weak (__malloc_fork_unlock_parent);
 
       /* Run things that want to run in the parent to restore it to
 	 normality.  Usually prepare hooks and parent hooks are
@@ -670,7 +670,7 @@ __fork (void)
       __sigemptyset (&_hurdsig_traced);
 
       /* Release malloc locks.  */
-      __malloc_fork_unlock_child ();
+      call_function_static_weak (__malloc_fork_unlock_child);
 
       /* Run things that want to run in the child task to set up.  */
       RUN_HOOK (_hurd_fork_child_hook, ());
diff --git a/sysdeps/nptl/fork.c b/sysdeps/nptl/fork.c
index 87aa91e..a5a8664 100644
--- a/sysdeps/nptl/fork.c
+++ b/sysdeps/nptl/fork.c
@@ -111,9 +111,10 @@ __libc_fork (void)
   _IO_list_lock ();
 
   /* Acquire malloc locks.  This needs to come last because fork
-     handlers may use malloc, and the libio list lock has an indirect
-     malloc dependency as well (via the getdelim function).  */
-  __malloc_fork_lock_parent ();
+     handlers may use malloc, and the libio list lock has an
+     indirect malloc dependency as well (via the getdelim
+     function).  */
+  call_function_static_weak (__malloc_fork_lock_parent);
 
 #ifndef NDEBUG
   pid_t ppid = THREAD_GETMEM (THREAD_SELF, tid);
@@ -173,7 +174,7 @@ __libc_fork (void)
 #endif
 
       /* Release malloc locks.  */
-      __malloc_fork_unlock_child ();
+      call_function_static_weak (__malloc_fork_unlock_child);
 
       /* Reset the file list.  These are recursive mutexes.  */
       fresetlockfiles ();
@@ -217,7 +218,7 @@ __libc_fork (void)
       THREAD_SETMEM (THREAD_SELF, pid, parentpid);
 
       /* Release malloc locks, parent process variant.  */
-      __malloc_fork_unlock_parent ();
+      call_function_static_weak (__malloc_fork_unlock_parent);
 
       /* We execute this even if the 'fork' call failed.  */
       _IO_list_unlock ();

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=a18c25e0ee00bb37c724123d8b187050e9a367a0

commit a18c25e0ee00bb37c724123d8b187050e9a367a0
Author: Florian Weimer <fweimer@redhat.com>
Date:   Tue Aug 16 11:06:13 2016 +0200

    nptl/tst-tls3-malloc: Force freeing of thread stacks
    
    It turns out that due to the reduced stack size in tst-tls3 and the
    (fixed) default stack cache size, allocated TLS variables are never
    freed, so the test coverage for tst-tls3-malloc is less than complete.
    This change increases the thread stack size for tst-tls3-malloc only,
    to make sure thread stacks and TLS variables are freed.

diff --git a/ChangeLog b/ChangeLog
index dbdb77c..c1a6363 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2016-08-16  Florian Weimer  <fweimer@redhat.com>
+
+	* nptl/tst-tls3.c (default_stack_size_in_mb, stack_size_in_mb):
+	New.
+	(do_test): Apply default_stack_size_in_mb if not set.
+	* nptl/tst-tls3-malloc.c (stack_size_in_mb): Override default.
+
 2016-08-02  Florian Weimer  <fweimer@redhat.com>
 
 	[BZ #19469]
diff --git a/nptl/tst-tls3-malloc.c b/nptl/tst-tls3-malloc.c
index 5eab3cd..8a580fa 100644
--- a/nptl/tst-tls3-malloc.c
+++ b/nptl/tst-tls3-malloc.c
@@ -19,6 +19,11 @@
 /* Reuse the test.  */
 #include "tst-tls3.c"
 
+/* Increase the thread stack size to 10 MiB, so that some thread
+   stacks are actually freed.  (The stack cache size is currently
+   hard-wired to 40 MiB in allocatestack.c.)  */
+static long stack_size_in_mb = 10;
+
 #include <sys/mman.h>
 
 /* Interpose a minimal malloc implementation.  This implementation
diff --git a/nptl/tst-tls3.c b/nptl/tst-tls3.c
index d504054..5d926ac 100644
--- a/nptl/tst-tls3.c
+++ b/nptl/tst-tls3.c
@@ -29,6 +29,11 @@
 
 #define THE_SIG SIGUSR1
 
+/* The stack size can be overriden.  With a sufficiently large stack
+   size, thread stacks for terminated threads are freed, but this does
+   not happen with the default size of 1 MiB.  */
+enum { default_stack_size_in_mb = 1 };
+static long stack_size_in_mb;
 
 #define N 10
 static pthread_t th[N];
@@ -72,6 +77,9 @@ int nsigs;
 int
 do_test (void)
 {
+  if (stack_size_in_mb == 0)
+    stack_size_in_mb = default_stack_size_in_mb;
+
   if ((uintptr_t) pthread_self () & (TCB_ALIGNMENT - 1))
     {
       puts ("initial thread's struct pthread not aligned enough");
@@ -127,7 +135,7 @@ do_test (void)
       exit (1);
     }
 
-  if (pthread_attr_setstacksize (&a, 1 * 1024 * 1024) != 0)
+  if (pthread_attr_setstacksize (&a, stack_size_in_mb * 1024 * 1024) != 0)
     {
       puts ("attr_setstacksize failed");
       return 1;

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=124ea50584d3d21fe4c2fe9383508efd0ac9010a

commit 124ea50584d3d21fe4c2fe9383508efd0ac9010a
Author: Florian Weimer <fweimer@redhat.com>
Date:   Tue Aug 2 17:01:02 2016 +0200

    malloc: Run tests without calling mallopt [BZ #19469]
    
    The compiled tests no longer refer to the mallopt symbol
    from their main functions.  (Some tests still call mallopt
    explicitly, which is fine.)

diff --git a/ChangeLog b/ChangeLog
index c5ad098..dbdb77c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2016-08-02  Florian Weimer  <fweimer@redhat.com>
+
+	[BZ #19469]
+	* malloc/Makefile (CPPFLAGS): Compile tests with
+	-DTEST_NO_MALLOPT.
+	* test-skeleton.c (main): Only call mallopt if !TEST_NO_MALLOPT.
+
 2016-08-03  Florian Weimer  <fweimer@redhat.com>
 
 	[BZ #17730]
diff --git a/malloc/Makefile b/malloc/Makefile
index 071f917..6e19a42 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -158,3 +158,7 @@ $(objpfx)libmemusage.so: $(libdl)
 
 # Extra dependencies
 $(foreach o,$(all-object-suffixes),$(objpfx)malloc$(o)): arena.c hooks.c
+
+# Compile the tests with a flag which suppresses the mallopt call in
+# the test skeleton.
+$(tests:%=$(objpfx)%.o): CPPFLAGS += -DTEST_NO_MALLOPT
diff --git a/test-skeleton.c b/test-skeleton.c
index 6a7fc42..5f1b543 100644
--- a/test-skeleton.c
+++ b/test-skeleton.c
@@ -236,8 +236,10 @@ main (int argc, char *argv[])
   unsigned int timeoutfactor = 1;
   pid_t termpid;
 
+#ifndef TEST_NO_MALLOPT
   /* Make uses of freed and uninitialized memory known.  */
   mallopt (M_PERTURB, 42);
+#endif
 
 #ifdef STDOUT_UNBUFFERED
   setbuf (stdout, NULL);

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=5a8692a64640bb648de6be9877dc072d728859bd

commit 5a8692a64640bb648de6be9877dc072d728859bd
Author: Florian Weimer <fweimer@redhat.com>
Date:   Wed Aug 3 16:16:57 2016 +0200

    elf: Do not use memalign for TCB/TLS blocks allocation [BZ #17730]
    
    Instead, call malloc and explicitly align the pointer.
    
    There is no external location to store the original (unaligned)
    pointer, and this commit increases the allocation size to store
    the pointer at a fixed location relative to the TCB pointer.
    
    The manual alignment means that some space goes unused which
    was previously made available for subsequent allocations.
    However, in the TLS_DTV_AT_TP case, the manual alignment code
    avoids aligning the pre-TCB to the TLS block alignment.  (Even
    while using memalign, the allocation had some unused padding
    in front.)
    
    This concludes the removal of memalign calls from the TLS code,
    and the new tst-tls3-malloc test verifies that only core malloc
    routines are used.

diff --git a/ChangeLog b/ChangeLog
index 0987532..c5ad098 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,21 @@
 2016-08-03  Florian Weimer  <fweimer@redhat.com>
 
 	[BZ #17730]
+	Avoid using memalign for TCB allocations.
+	* elf/dl-tls.c (tcb_to_pointer_to_free_location): New.
+	(_dl_allocate_tls_storage): Use malloc and manual alignment.
+	Avoid alignment gap in the TLS_DTV_AT_TP case.
+	(_dl_deallocate_tls): Use tcb_to_pointer_to_free_location to
+	determine the pointer to free.
+	* nptl/tst-tls3-malloc.c: New test.
+	* nptl/Makefile (tests): Add it.
+	(tst-tls3-malloc): Link with libdl, libpthread.
+	(LDFLAGS-tst-tls3-malloc): Set.
+	(tst-tls3-malloc.out): Depend on DSO used in test.
+
+2016-08-03  Florian Weimer  <fweimer@redhat.com>
+
+	[BZ #17730]
 	Avoid using memalign for TLS allocations.
 	* sysdeps/generic/dl-dtv.h (struct dtv_pointer): New.  Replaces
 	is_static member with to_free member.
diff --git a/elf/dl-tls.c b/elf/dl-tls.c
index 2937213..2e2cb35 100644
--- a/elf/dl-tls.c
+++ b/elf/dl-tls.c
@@ -347,6 +347,22 @@ _dl_get_tls_static_info (size_t *sizep, size_t *alignp)
   *alignp = GL(dl_tls_static_align);
 }
 
+/* Derive the location of the pointer to the start of the original
+   allocation (before alignment) from the pointer to the TCB.  */
+static inline void **
+tcb_to_pointer_to_free_location (void *tcb)
+{
+#if TLS_TCB_AT_TP
+  /* The TCB follows the TLS blocks, and the pointer to the front
+     follows the TCB.  */
+  void **original_pointer_location = tcb + TLS_TCB_SIZE;
+#elif TLS_DTV_AT_TP
+  /* The TCB comes first, preceded by the pre-TCB, and the pointer is
+     before that.  */
+  void **original_pointer_location = tcb - TLS_PRE_TCB_SIZE - sizeof (void *);
+#endif
+  return original_pointer_location;
+}
 
 void *
 internal_function
@@ -359,39 +375,50 @@ _dl_allocate_tls_storage (void)
   /* Memory layout is:
      [ TLS_PRE_TCB_SIZE ] [ TLS_TCB_SIZE ] [ TLS blocks ]
 			  ^ This should be returned.  */
-  size += (TLS_PRE_TCB_SIZE + GL(dl_tls_static_align) - 1)
-	  & ~(GL(dl_tls_static_align) - 1);
+  size += TLS_PRE_TCB_SIZE;
 #endif
 
-  /* Allocate a correctly aligned chunk of memory.  */
-  result = __libc_memalign (GL(dl_tls_static_align), size);
-  if (__builtin_expect (result != NULL, 1))
-    {
-      /* Allocate the DTV.  */
-      void *allocated = result;
+  /* Perform the allocation.  Reserve space for the required alignment
+     and the pointer to the original allocation.  */
+  size_t alignment = GL(dl_tls_static_align);
+  void *allocated = malloc (size + alignment + sizeof (void *));
+  if (__glibc_unlikely (allocated == NULL))
+    return NULL;
 
+  /* Perform alignment and allocate the DTV.  */
 #if TLS_TCB_AT_TP
-      /* The TCB follows the TLS blocks.  */
-      result = (char *) result + size - TLS_TCB_SIZE;
-
-      /* Clear the TCB data structure.  We can't ask the caller (i.e.
-	 libpthread) to do it, because we will initialize the DTV et al.  */
-      memset (result, '\0', TLS_TCB_SIZE);
+  /* The TCB follows the TLS blocks, which determine the alignment.
+     (TCB alignment requirements have been taken into account when
+     calculating GL(dl_tls_static_align).)  */
+  void *aligned = (void *) roundup ((uintptr_t) allocated, alignment);
+  result = aligned + size - TLS_TCB_SIZE;
+
+  /* Clear the TCB data structure.  We can't ask the caller (i.e.
+     libpthread) to do it, because we will initialize the DTV et al.  */
+  memset (result, '\0', TLS_TCB_SIZE);
 #elif TLS_DTV_AT_TP
-      result = (char *) result + size - GL(dl_tls_static_size);
-
-      /* Clear the TCB data structure and TLS_PRE_TCB_SIZE bytes before it.
-	 We can't ask the caller (i.e. libpthread) to do it, because we will
-	 initialize the DTV et al.  */
-      memset ((char *) result - TLS_PRE_TCB_SIZE, '\0',
-	      TLS_PRE_TCB_SIZE + TLS_TCB_SIZE);
+  /* Pre-TCB and TCB come before the TLS blocks.  The layout computed
+     in _dl_determine_tlsoffset assumes that the TCB is aligned to the
+     TLS block alignment, and not just the TLS blocks after it.  This
+     can leave an unused alignment gap between the TCB and the TLS
+     blocks.  */
+  result = (void *) roundup
+    (sizeof (void *) + TLS_PRE_TCB_SIZE + (uintptr_t) allocated,
+     alignment);
+
+  /* Clear the TCB data structure and TLS_PRE_TCB_SIZE bytes before
+     it.  We can't ask the caller (i.e. libpthread) to do it, because
+     we will initialize the DTV et al.  */
+  memset (result - TLS_PRE_TCB_SIZE, '\0', TLS_PRE_TCB_SIZE + TLS_TCB_SIZE);
 #endif
 
-      result = allocate_dtv (result);
-      if (result == NULL)
-	free (allocated);
-    }
+  /* Record the value of the original pointer for later
+     deallocation.  */
+  *tcb_to_pointer_to_free_location (result) = allocated;
 
+  result = allocate_dtv (result);
+  if (result == NULL)
+    free (allocated);
   return result;
 }
 
@@ -558,17 +585,7 @@ _dl_deallocate_tls (void *tcb, bool dealloc_tcb)
     free (dtv - 1);
 
   if (dealloc_tcb)
-    {
-#if TLS_TCB_AT_TP
-      /* The TCB follows the TLS blocks.  Back up to free the whole block.  */
-      tcb -= GL(dl_tls_static_size) - TLS_TCB_SIZE;
-#elif TLS_DTV_AT_TP
-      /* Back up the TLS_PRE_TCB_SIZE bytes.  */
-      tcb -= (TLS_PRE_TCB_SIZE + GL(dl_tls_static_align) - 1)
-	     & ~(GL(dl_tls_static_align) - 1);
-#endif
-      free (tcb);
-    }
+    free (*tcb_to_pointer_to_free_location (tcb));
 }
 rtld_hidden_def (_dl_deallocate_tls)
 
diff --git a/nptl/Makefile b/nptl/Makefile
index 43d8510..20716b0 100644
--- a/nptl/Makefile
+++ b/nptl/Makefile
@@ -301,8 +301,8 @@ tests += tst-cancelx2 tst-cancelx3 tst-cancelx4 tst-cancelx5 \
 	 tst-oncex3 tst-oncex4
 endif
 ifeq ($(build-shared),yes)
-tests += tst-atfork2 tst-tls3 tst-tls4 tst-tls5 tst-_res1 tst-fini1 \
-	 tst-stackguard1
+tests += tst-atfork2 tst-tls3 tst-tls3-malloc tst-tls4 tst-tls5 tst-_res1 \
+	 tst-fini1 tst-stackguard1
 tests-nolibpthread += tst-fini1
 ifeq ($(have-z-execstack),yes)
 tests += tst-execstack
@@ -500,6 +500,10 @@ LDFLAGS-tst-tls3 = -rdynamic
 $(objpfx)tst-tls3.out: $(objpfx)tst-tls3mod.so
 $(objpfx)tst-tls3mod.so: $(shared-thread-library)
 
+$(objpfx)tst-tls3-malloc: $(libdl) $(shared-thread-library)
+LDFLAGS-tst-tls3-malloc = -rdynamic
+$(objpfx)tst-tls3-malloc.out: $(objpfx)tst-tls3mod.so
+
 $(objpfx)tst-tls4: $(libdl) $(shared-thread-library)
 $(objpfx)tst-tls4.out: $(objpfx)tst-tls4moda.so $(objpfx)tst-tls4modb.so
 
diff --git a/nptl/tst-tls3-malloc.c b/nptl/tst-tls3-malloc.c
new file mode 100644
index 0000000..5eab3cd
--- /dev/null
+++ b/nptl/tst-tls3-malloc.c
@@ -0,0 +1,176 @@
+/* Test TLS allocation with an interposed malloc.
+   Copyright (C) 2016 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/>.  */
+
+/* Reuse the test.  */
+#include "tst-tls3.c"
+
+#include <sys/mman.h>
+
+/* Interpose a minimal malloc implementation.  This implementation
+   deliberately interposes just a restricted set of symbols, to detect
+   if the TLS code bypasses the interposed malloc.  */
+
+/* Lock to guard malloc internals.  */
+static pthread_mutex_t malloc_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/* Information about an allocation chunk.  */
+struct malloc_chunk
+{
+  /* Start of the allocation.  */
+  void *start;
+  /* Size of the allocation.  */
+  size_t size;
+};
+
+enum { malloc_chunk_count = 1000 };
+static struct malloc_chunk chunks[malloc_chunk_count];
+
+/* Lock the malloc lock.  */
+static void
+xlock (void)
+{
+  int ret = pthread_mutex_lock (&malloc_lock);
+  if (ret != 0)
+    {
+      errno = ret;
+      printf ("error: pthread_mutex_lock: %m\n");
+      _exit (1);
+    }
+}
+
+/* Unlock the malloc lock.  */
+static void
+xunlock (void)
+{
+  int ret = pthread_mutex_unlock (&malloc_lock);
+  if (ret != 0)
+    {
+      errno = ret;
+      printf ("error: pthread_mutex_unlock: %m\n");
+      _exit (1);
+    }
+}
+
+/* Internal malloc without locking and registration.  */
+static void *
+malloc_internal (size_t size)
+{
+  void *result = mmap (NULL, size, PROT_READ | PROT_WRITE,
+                       MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+  if (result == MAP_FAILED)
+    {
+      printf ("error: mmap: %m\n");
+      _exit (1);
+    }
+  return result;
+}
+
+void *
+malloc (size_t size)
+{
+  if (size == 0)
+    size = 1;
+  xlock ();
+  void *result = malloc_internal (size);
+  for (int i = 0; i < malloc_chunk_count; ++i)
+    if (chunks[i].start == NULL)
+      {
+        chunks[i].start = result;
+        chunks[i].size = size;
+        xunlock ();
+        return result;
+      }
+  xunlock ();
+  printf ("error: no place to store chunk pointer\n");
+  _exit (1);
+}
+
+void *
+calloc (size_t a, size_t b)
+{
+  if (b != 0 && a > SIZE_MAX / b)
+    return NULL;
+  /* malloc uses mmap, which provides zeroed memory.  */
+  return malloc (a * b);
+}
+
+static void
+xunmap (void *ptr, size_t size)
+{
+  int ret = munmap (ptr, size);
+  if (ret < 0)
+    {
+      printf ("error: munmap (%p, %zu) failed: %m\n", ptr, size);
+      _exit (1);
+    }
+}
+
+void
+free (void *ptr)
+{
+  if (ptr == NULL)
+    return;
+
+  xlock ();
+  for (int i = 0; i < malloc_chunk_count; ++i)
+    if (chunks[i].start == ptr)
+      {
+        xunmap (ptr, chunks[i].size);
+        chunks[i] = (struct malloc_chunk) {};
+        xunlock ();
+        return;
+      }
+  xunlock ();
+  printf ("error: tried to free non-allocated pointer %p\n", ptr);
+  _exit (1);
+}
+
+void *
+realloc (void *old, size_t size)
+{
+  if (old != NULL)
+    {
+      xlock ();
+      for (int i = 0; i < malloc_chunk_count; ++i)
+        if (chunks[i].start == old)
+          {
+            size_t old_size = chunks[i].size;
+            void *result;
+            if (old_size < size)
+              {
+                result = malloc_internal (size);
+                /* Reuse the slot for the new allocation.  */
+                memcpy (result, old, old_size);
+                xunmap (old, old_size);
+                chunks[i].start = result;
+                chunks[i].size = size;
+              }
+            else
+              /* Old size is not smaller, so reuse the old
+                 allocation.  */
+              result = old;
+            xunlock ();
+            return result;
+          }
+      xunlock ();
+      printf ("error: tried to realloc non-allocated pointer %p\n", old);
+      _exit (1);
+    }
+  else
+    return malloc (size);
+}

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=6a277a415c127f6ee9a70548c899a7ac536f4288

commit 6a277a415c127f6ee9a70548c899a7ac536f4288
Author: Florian Weimer <fweimer@redhat.com>
Date:   Wed Aug 3 16:15:38 2016 +0200

    elf: Avoid using memalign for TLS allocations [BZ #17730]
    
    Instead of a flag which indicates the pointer can be freed, dtv_t
    now includes the pointer which should be freed.  Due to padding,
    the size of dtv_t does not increase.
    
    To avoid using memalign, the new allocate_dtv_entry function
    allocates a sufficiently large buffer so that a sub-buffer
    can be found in it which starts with an aligned pointer.  Both
    the aligned and original pointers are kept, the latter for calling
    free later.

diff --git a/ChangeLog b/ChangeLog
index a5b5bd1..0987532 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2016-08-03  Florian Weimer  <fweimer@redhat.com>
+
+	[BZ #17730]
+	Avoid using memalign for TLS allocations.
+	* sysdeps/generic/dl-dtv.h (struct dtv_pointer): New.  Replaces
+	is_static member with to_free member.
+	(union dtv): Use struct dtv_pointer.
+	* csu/libc-tls.c (__libc_setup_tls): Set to_free member of struct
+	dtv_pointer instead of is_static.
+	* elf/dl-tls.c (_dl_allocate_tls_init): Likewise.
+	(_dl_deallocate_tls): Free to_free member of struct dtv_pointer
+	instead of val.
+	(allocate_dtv_entry): New function.
+	(allocate_and_init): Return struct dtv_pointer.  Call
+	allocate_dtv_entry instead of __libc_memalign.
+	(_dl_update_slotinfo): Free to_free member of struct dtv_pointer
+	instead of val.
+	(tls_get_addr_tail): Set to_free member of struct dtv_pointer
+	instead of is_static.  Adjust call to allocate_and_init.
+	* nptl/allocatestack.c (get_cached_stack): Free to_free member of
+	struct dtv_pointer instead of val.
+
 2015-03-17  Alexandre Oliva <aoliva@redhat.com>
 
 	[BZ #17090]
diff --git a/csu/libc-tls.c b/csu/libc-tls.c
index 64d1779..b0f4c57 100644
--- a/csu/libc-tls.c
+++ b/csu/libc-tls.c
@@ -172,7 +172,7 @@ __libc_setup_tls (size_t tcbsize, size_t tcbalign)
 #else
 # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
 #endif
-  _dl_static_dtv[2].pointer.is_static = true;
+  _dl_static_dtv[2].pointer.to_free = NULL;
   /* sbrk gives us zero'd memory, so we don't need to clear the remainder.  */
   memcpy (_dl_static_dtv[2].pointer.val, initimage, filesz);
 
diff --git a/elf/dl-tls.c b/elf/dl-tls.c
index 20c7e33..2937213 100644
--- a/elf/dl-tls.c
+++ b/elf/dl-tls.c
@@ -494,7 +494,7 @@ _dl_allocate_tls_init (void *result)
 	  maxgen = MAX (maxgen, listp->slotinfo[cnt].gen);
 
 	  dtv[map->l_tls_modid].pointer.val = TLS_DTV_UNALLOCATED;
-	  dtv[map->l_tls_modid].pointer.is_static = false;
+	  dtv[map->l_tls_modid].pointer.to_free = NULL;
 
 	  if (map->l_tls_offset == NO_TLS_OFFSET
 	      || map->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET)
@@ -551,9 +551,7 @@ _dl_deallocate_tls (void *tcb, bool dealloc_tcb)
 
   /* We need to free the memory allocated for non-static TLS.  */
   for (size_t cnt = 0; cnt < dtv[-1].counter; ++cnt)
-    if (! dtv[1 + cnt].pointer.is_static
-	&& dtv[1 + cnt].pointer.val != TLS_DTV_UNALLOCATED)
-      free (dtv[1 + cnt].pointer.val);
+    free (dtv[1 + cnt].pointer.to_free);
 
   /* The array starts with dtv[-1].  */
   if (dtv != GL(dl_initial_dtv))
@@ -594,21 +592,49 @@ rtld_hidden_def (_dl_deallocate_tls)
 #  define GET_ADDR_OFFSET ti->ti_offset
 # endif
 
+/* Allocate one DTV entry.  */
+static struct dtv_pointer
+allocate_dtv_entry (size_t alignment, size_t size)
+{
+  if (powerof2 (alignment) && alignment <= _Alignof (long double))
+    {
+      /* The alignment is supported by malloc.  */
+      void *ptr = malloc (size);
+      return (struct dtv_pointer) { ptr, ptr };
+    }
 
-static void *
+  /* Emulate memalign to by manually aligning a pointer returned by
+     malloc.  First compute the size with an overflow check.  */
+  size_t alloc_size = size + alignment;
+  if (alloc_size < size)
+    return (struct dtv_pointer) {};
+
+  /* Perform the allocation.  This is the pointer we need to free
+     later.  */
+  void *start = malloc (alloc_size);
+  if (start == NULL)
+    return (struct dtv_pointer) {};
+
+  /* Find the aligned position within the larger allocation.  */
+  void *aligned = (void *) roundup ((uintptr_t) start, alignment);
+
+  return (struct dtv_pointer) { .val = aligned, .to_free = start };
+}
+
+static struct dtv_pointer
 allocate_and_init (struct link_map *map)
 {
-  void *newp;
-
-  newp = __libc_memalign (map->l_tls_align, map->l_tls_blocksize);
-  if (newp == NULL)
+  struct dtv_pointer result = allocate_dtv_entry
+    (map->l_tls_align, map->l_tls_blocksize);
+  if (result.val == NULL)
     oom ();
 
   /* Initialize the memory.  */
-  memset (__mempcpy (newp, map->l_tls_initimage, map->l_tls_initimage_size),
+  memset (__mempcpy (result.val, map->l_tls_initimage,
+		     map->l_tls_initimage_size),
 	  '\0', map->l_tls_blocksize - map->l_tls_initimage_size);
 
-  return newp;
+  return result;
 }
 
 
@@ -678,12 +704,9 @@ _dl_update_slotinfo (unsigned long int req_modid)
 		    {
 		      /* If this modid was used at some point the memory
 			 might still be allocated.  */
-		      if (! dtv[total + cnt].pointer.is_static
-			  && (dtv[total + cnt].pointer.val
-			      != TLS_DTV_UNALLOCATED))
-			free (dtv[total + cnt].pointer.val);
+		      free (dtv[total + cnt].pointer.to_free);
 		      dtv[total + cnt].pointer.val = TLS_DTV_UNALLOCATED;
-		      dtv[total + cnt].pointer.is_static = false;
+		      dtv[total + cnt].pointer.to_free = NULL;
 		    }
 
 		  continue;
@@ -708,16 +731,9 @@ _dl_update_slotinfo (unsigned long int req_modid)
 		 dtv entry free it.  */
 	      /* XXX Ideally we will at some point create a memory
 		 pool.  */
-	      if (! dtv[modid].pointer.is_static
-		  && dtv[modid].pointer.val != TLS_DTV_UNALLOCATED)
-		/* Note that free is called for NULL is well.  We
-		   deallocate even if it is this dtv entry we are
-		   supposed to load.  The reason is that we call
-		   memalign and not malloc.  */
-		free (dtv[modid].pointer.val);
-
+	      free (dtv[modid].pointer.to_free);
 	      dtv[modid].pointer.val = TLS_DTV_UNALLOCATED;
-	      dtv[modid].pointer.is_static = false;
+	      dtv[modid].pointer.to_free = NULL;
 
 	      if (modid == req_modid)
 		the_map = map;
@@ -780,7 +796,7 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map)
 #endif
 	  __rtld_lock_unlock_recursive (GL(dl_load_lock));
 
-	  dtv[GET_ADDR_MODULE].pointer.is_static = true;
+	  dtv[GET_ADDR_MODULE].pointer.to_free = NULL;
 	  dtv[GET_ADDR_MODULE].pointer.val = p;
 
 	  return (char *) p + GET_ADDR_OFFSET;
@@ -788,10 +804,11 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map)
       else
 	__rtld_lock_unlock_recursive (GL(dl_load_lock));
     }
-  void *p = dtv[GET_ADDR_MODULE].pointer.val = allocate_and_init (the_map);
-  assert (!dtv[GET_ADDR_MODULE].pointer.is_static);
+  struct dtv_pointer result = allocate_and_init (the_map);
+  dtv[GET_ADDR_MODULE].pointer = result;
+  assert (result.to_free != NULL);
 
-  return (char *) p + GET_ADDR_OFFSET;
+  return (char *) result.val + GET_ADDR_OFFSET;
 }
 
 
diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
index 8e620c4..ce03576 100644
--- a/nptl/allocatestack.c
+++ b/nptl/allocatestack.c
@@ -244,9 +244,7 @@ get_cached_stack (size_t *sizep, void **memp)
   /* Clear the DTV.  */
   dtv_t *dtv = GET_DTV (TLS_TPADJ (result));
   for (size_t cnt = 0; cnt < dtv[-1].counter; ++cnt)
-    if (! dtv[1 + cnt].pointer.is_static
-	&& dtv[1 + cnt].pointer.val != TLS_DTV_UNALLOCATED)
-      free (dtv[1 + cnt].pointer.val);
+    free (dtv[1 + cnt].pointer.to_free);
   memset (dtv, '\0', (dtv[-1].counter + 1) * sizeof (dtv_t));
 
   /* Re-initialize the TLS.  */
diff --git a/sysdeps/generic/dl-dtv.h b/sysdeps/generic/dl-dtv.h
index 36c5c58..39d8fe2 100644
--- a/sysdeps/generic/dl-dtv.h
+++ b/sysdeps/generic/dl-dtv.h
@@ -19,15 +19,17 @@
 #ifndef _DL_DTV_H
 #define _DL_DTV_H
 
+struct dtv_pointer
+{
+  void *val;                    /* Pointer to data, or TLS_DTV_UNALLOCATED.  */
+  void *to_free;                /* Unaligned pointer, for deallocation.  */
+};
+
 /* Type for the dtv.  */
 typedef union dtv
 {
   size_t counter;
-  struct
-  {
-    void *val;
-    bool is_static;
-  } pointer;
+  struct dtv_pointer pointer;
 } dtv_t;
 
 /* Value used for dtv entries for which the allocation is delayed.  */

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=9e1e8f68d59e33baa0eef9e8690d3cbaa5f67a24

commit 9e1e8f68d59e33baa0eef9e8690d3cbaa5f67a24
Author: Alexandre Oliva <aoliva@redhat.com>
Date:   Tue Mar 17 01:14:11 2015 -0300

    Fix DTV race, assert, DTV_SURPLUS Static TLS limit, and nptl_db garbage
    
    for  ChangeLog
    
    	[BZ #17090]
    	[BZ #17620]
    	[BZ #17621]
    	[BZ #17628]
    	* NEWS: Update.
    	* elf/dl-tls.c (_dl_update_slotinfo): Clean up outdated DTV
    	entries with Static TLS too.  Skip entries past the end of the
    	allocated DTV, from Alan Modra.
    	(tls_get_addr_tail): Update to glibc_likely/unlikely.  Move
    	Static TLS DTV entry set up from...
    	 (_dl_allocate_tls_init): ... here (fix modid assertion), ...
    	* elf/dl-reloc.c (_dl_nothread_init_static_tls): ... here...
    	* nptl/allocatestack.c (init_one_static_tls): ... and here...
    	* elf/dlopen.c (dl_open_worker): Drop l_tls_modid upper bound
    	for Static TLS.
    	* elf/tlsdeschtab.h (map_generation): Return size_t.  Check
    	that the slot we find is associated with the given map before
    	using its generation count.
    	* nptl_db/db_info.c: Include ldsodefs.h.
    	(rtld_global, dtv_slotinfo_list, dtv_slotinfo): New typedefs.
    	* nptl_db/structs.def (DB_RTLD_VARIABLE): New macro.
    	(DB_MAIN_VARIABLE, DB_RTLD_GLOBAL_FIELD): Likewise.
    	(link_map::l_tls_offset): New struct field.
    	(dtv_t::counter): Likewise.
    	(rtld_global): New struct.
    	(_rtld_global): New rtld variable.
    	(dl_tls_dtv_slotinfo_list): New rtld global field.
    	(dtv_slotinfo_list): New struct.
    	(dtv_slotinfo): Likewise.
    	* nptl_db/td_symbol_list.c: Drop gnu/lib-names.h include.
    	(td_lookup): Rename to...
    	(td_mod_lookup): ... this.  Use new mod parameter instead of
    	LIBPTHREAD_SO.
    	* nptl_db/td_thr_tlsbase.c: Include link.h.
    	(dtv_slotinfo_list, dtv_slotinfo): New functions.
    	(td_thr_tlsbase): Check DTV generation.  Compute Static TLS
    	addresses even if the DTV is out of date or missing them.
    	* nptl_db/fetch-value.c (_td_locate_field): Do not refuse to
    	index zero-length arrays.
    	* nptl_db/thread_dbP.h: Include gnu/lib-names.h.
    	(td_lookup): Make it a macro implemented in terms of...
    	(td_mod_lookup): ... this declaration.
    	* nptl_db/db-symbols.awk (DB_RTLD_VARIABLE): Override.
    	(DB_MAIN_VARIABLE): Likewise.

diff --git a/ChangeLog b/ChangeLog
index 3408f9c..a5b5bd1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,50 @@
+2015-03-17  Alexandre Oliva <aoliva@redhat.com>
+
+	[BZ #17090]
+	[BZ #17620]
+	[BZ #17621]
+	[BZ #17628]
+	* NEWS: Update.
+	* elf/dl-tls.c (_dl_update_slotinfo): Clean up outdated DTV
+	entries with Static TLS too.  Skip entries past the end of the
+	allocated DTV, from Alan Modra.
+	(tls_get_addr_tail): Update to glibc_likely/unlikely.  Move
+	Static TLS DTV entry set up from...
+	(_dl_allocate_tls_init): ... here (fix modid assertion), ...
+	* elf/dl-reloc.c (_dl_nothread_init_static_tls): ... here...
+	* nptl/allocatestack.c (init_one_static_tls): ... and here...
+	* elf/dlopen.c (dl_open_worker): Drop l_tls_modid upper bound
+	for Static TLS.
+	* elf/tlsdeschtab.h (map_generation): Return size_t.  Check
+	that the slot we find is associated with the given map before
+	using its generation count.
+	* nptl_db/db_info.c: Include ldsodefs.h.
+	(rtld_global, dtv_slotinfo_list, dtv_slotinfo): New typedefs.
+	* nptl_db/structs.def (DB_RTLD_VARIABLE): New macro.
+	(DB_MAIN_VARIABLE, DB_RTLD_GLOBAL_FIELD): Likewise.
+	(link_map::l_tls_offset): New struct field.
+	(dtv_t::counter): Likewise.
+	(rtld_global): New struct.
+	(_rtld_global): New rtld variable.
+	(dl_tls_dtv_slotinfo_list): New rtld global field.
+	(dtv_slotinfo_list): New struct.
+	(dtv_slotinfo): Likewise.
+	* nptl_db/td_symbol_list.c: Drop gnu/lib-names.h include.
+	(td_lookup): Rename to...
+	(td_mod_lookup): ... this.  Use new mod parameter instead of
+	LIBPTHREAD_SO.
+	* nptl_db/td_thr_tlsbase.c: Include link.h.
+	(dtv_slotinfo_list, dtv_slotinfo): New functions.
+	(td_thr_tlsbase): Check DTV generation.  Compute Static TLS
+	addresses even if the DTV is out of date or missing them.
+	* nptl_db/fetch-value.c (_td_locate_field): Do not refuse to
+	index zero-length arrays.
+	* nptl_db/thread_dbP.h: Include gnu/lib-names.h.
+	(td_lookup): Make it a macro implemented in terms of...
+	(td_mod_lookup): ... this declaration.
+	* nptl_db/db-symbols.awk (DB_RTLD_VARIABLE): Override.
+	(DB_MAIN_VARIABLE): Likewise.
+
 2016-06-20  Florian Weimer  <fweimer@redhat.com>
 
 	Consolidate machine-agnostic DTV definitions in <dl-dtv.h>.
diff --git a/NEWS b/NEWS
index 147d5ff..f6f8685 100644
--- a/NEWS
+++ b/NEWS
@@ -9,8 +9,8 @@ Version 2.21.1
 
 * The following bugs are resolved with this release:
 
-  17269, 17905, 17949, 18007, 18032, 18080, 18240, 18287, 18508, 18694,
-  18887, 18985, 19048, 19682.
+  17090, 17269, 17620, 17621, 17628, 17905, 17949, 18007, 18032, 18080,
+  18240, 18287, 18508, 18694, 18887, 18985, 19048, 19682.
 
 * A stack-based buffer overflow was found in libresolv when invoked from
   libnss_dns, allowing specially crafted DNS responses to seize control
diff --git a/elf/dl-open.c b/elf/dl-open.c
index 47b4cb5..0dbe07f 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -533,17 +533,7 @@ TLS generation counter wrapped!  Please report this."));
 	  && 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"));
-
+	     now, but we can delay updating the DTV.  */
 	  imap->l_need_tls_init = 0;
 #ifdef SHARED
 	  /* Update the slot information data for at least the
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
index 9c2705d..2e40ced 100644
--- a/elf/dl-reloc.c
+++ b/elf/dl-reloc.c
@@ -136,12 +136,6 @@ _dl_nothread_init_static_tls (struct link_map *map)
 # error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
 #endif
 
-  /* Fill in the DTV slot so that a later LD/GD access will find it.  */
-  dtv_t *dtv = THREAD_DTV ();
-  assert (map->l_tls_modid <= dtv[-1].counter);
-  dtv[map->l_tls_modid].pointer.val = dest;
-  dtv[map->l_tls_modid].pointer.is_static = true;
-
   /* Initialize the memory.  */
   memset (__mempcpy (dest, map->l_tls_initimage, map->l_tls_initimage_size),
 	  '\0', map->l_tls_blocksize - map->l_tls_initimage_size);
diff --git a/elf/dl-tls.c b/elf/dl-tls.c
index 9d36d96..20c7e33 100644
--- a/elf/dl-tls.c
+++ b/elf/dl-tls.c
@@ -493,17 +493,14 @@ _dl_allocate_tls_init (void *result)
 	  assert (listp->slotinfo[cnt].gen <= GL(dl_tls_generation));
 	  maxgen = MAX (maxgen, listp->slotinfo[cnt].gen);
 
+	  dtv[map->l_tls_modid].pointer.val = TLS_DTV_UNALLOCATED;
+	  dtv[map->l_tls_modid].pointer.is_static = false;
+
 	  if (map->l_tls_offset == NO_TLS_OFFSET
 	      || map->l_tls_offset == FORCED_DYNAMIC_TLS_OFFSET)
-	    {
-	      /* For dynamically loaded modules we simply store
-		 the value indicating deferred allocation.  */
-	      dtv[map->l_tls_modid].pointer.val = TLS_DTV_UNALLOCATED;
-	      dtv[map->l_tls_modid].pointer.is_static = false;
-	      continue;
-	    }
+	    continue;
 
-	  assert (map->l_tls_modid == cnt);
+	  assert (map->l_tls_modid == total + cnt);
 	  assert (map->l_tls_blocksize >= map->l_tls_initimage_size);
 #if TLS_TCB_AT_TP
 	  assert ((size_t) map->l_tls_offset >= map->l_tls_blocksize);
@@ -515,8 +512,6 @@ _dl_allocate_tls_init (void *result)
 #endif
 
 	  /* Copy the initialization image and clear the BSS part.  */
-	  dtv[map->l_tls_modid].pointer.val = dest;
-	  dtv[map->l_tls_modid].pointer.is_static = true;
 	  memset (__mempcpy (dest, map->l_tls_initimage,
 			     map->l_tls_initimage_size), '\0',
 		  map->l_tls_blocksize - map->l_tls_initimage_size);
@@ -679,13 +674,16 @@ _dl_update_slotinfo (unsigned long int req_modid)
 	      struct link_map *map = listp->slotinfo[cnt].map;
 	      if (map == NULL)
 		{
-		  /* If this modid was used at some point the memory
-		     might still be allocated.  */
-		  if (! dtv[total + cnt].pointer.is_static
-		      && dtv[total + cnt].pointer.val != TLS_DTV_UNALLOCATED)
+		  if (dtv[-1].counter >= total + cnt)
 		    {
-		      free (dtv[total + cnt].pointer.val);
+		      /* If this modid was used at some point the memory
+			 might still be allocated.  */
+		      if (! dtv[total + cnt].pointer.is_static
+			  && (dtv[total + cnt].pointer.val
+			      != TLS_DTV_UNALLOCATED))
+			free (dtv[total + cnt].pointer.val);
 		      dtv[total + cnt].pointer.val = TLS_DTV_UNALLOCATED;
+		      dtv[total + cnt].pointer.is_static = false;
 		    }
 
 		  continue;
@@ -718,10 +716,8 @@ _dl_update_slotinfo (unsigned long int req_modid)
 		   memalign and not malloc.  */
 		free (dtv[modid].pointer.val);
 
-	      /* This module is loaded dynamically- We defer memory
-		 allocation.  */
-	      dtv[modid].pointer.is_static = false;
 	      dtv[modid].pointer.val = TLS_DTV_UNALLOCATED;
+	      dtv[modid].pointer.is_static = false;
 
 	      if (modid == req_modid)
 		the_map = map;
@@ -759,13 +755,12 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map)
       the_map = listp->slotinfo[idx].map;
     }
 
- again:
   /* Make sure that, if a dlopen running in parallel forces the
      variable into static storage, we'll wait until the address in the
      static TLS block is set up, and use that.  If we're undecided
      yet, make sure we make the decision holding the lock as well.  */
-  if (__builtin_expect (the_map->l_tls_offset
-			!= FORCED_DYNAMIC_TLS_OFFSET, 0))
+  if (__glibc_unlikely (the_map->l_tls_offset
+			!= FORCED_DYNAMIC_TLS_OFFSET))
     {
       __rtld_lock_lock_recursive (GL(dl_load_lock));
       if (__glibc_likely (the_map->l_tls_offset == NO_TLS_OFFSET))
@@ -773,22 +768,28 @@ tls_get_addr_tail (GET_ADDR_ARGS, dtv_t *dtv, struct link_map *the_map)
 	  the_map->l_tls_offset = FORCED_DYNAMIC_TLS_OFFSET;
 	  __rtld_lock_unlock_recursive (GL(dl_load_lock));
 	}
-      else
+      else if (__glibc_likely (the_map->l_tls_offset
+			       != FORCED_DYNAMIC_TLS_OFFSET))
 	{
+#if TLS_TCB_AT_TP
+	  void *p = (char *) THREAD_SELF - the_map->l_tls_offset;
+#elif TLS_DTV_AT_TP
+	  void *p = (char *) THREAD_SELF + the_map->l_tls_offset + TLS_PRE_TCB_SIZE;
+#else
+# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
+#endif
 	  __rtld_lock_unlock_recursive (GL(dl_load_lock));
-	  if (__builtin_expect (the_map->l_tls_offset
-				!= FORCED_DYNAMIC_TLS_OFFSET, 1))
-	    {
-	      void *p = dtv[GET_ADDR_MODULE].pointer.val;
-	      if (__glibc_unlikely (p == TLS_DTV_UNALLOCATED))
-		goto again;
 
-	      return (char *) p + GET_ADDR_OFFSET;
-	    }
+	  dtv[GET_ADDR_MODULE].pointer.is_static = true;
+	  dtv[GET_ADDR_MODULE].pointer.val = p;
+
+	  return (char *) p + GET_ADDR_OFFSET;
 	}
+      else
+	__rtld_lock_unlock_recursive (GL(dl_load_lock));
     }
   void *p = dtv[GET_ADDR_MODULE].pointer.val = allocate_and_init (the_map);
-  dtv[GET_ADDR_MODULE].pointer.is_static = false;
+  assert (!dtv[GET_ADDR_MODULE].pointer.is_static);
 
   return (char *) p + GET_ADDR_OFFSET;
 }
diff --git a/elf/tlsdeschtab.h b/elf/tlsdeschtab.h
index d7e7955..d13b4e5 100644
--- a/elf/tlsdeschtab.h
+++ b/elf/tlsdeschtab.h
@@ -42,7 +42,7 @@ eq_tlsdesc (void *p, void *q)
   return tdp->tlsinfo.ti_offset == tdq->tlsinfo.ti_offset;
 }
 
-inline static int
+inline static size_t
 map_generation (struct link_map *map)
 {
   size_t idx = map->l_tls_modid;
@@ -58,7 +58,7 @@ map_generation (struct link_map *map)
 	     we can assume that, if the generation count is zero, we
 	     still haven't determined the generation count for this
 	     module.  */
-	  if (listp->slotinfo[idx].gen)
+	  if (listp->slotinfo[idx].map == map && listp->slotinfo[idx].gen)
 	    return listp->slotinfo[idx].gen;
 	  else
 	    break;
diff --git a/nptl/allocatestack.c b/nptl/allocatestack.c
index 3c8e046..8e620c4 100644
--- a/nptl/allocatestack.c
+++ b/nptl/allocatestack.c
@@ -1190,7 +1190,6 @@ __nptl_setxid (struct xid_command *cmdp)
 static inline void __attribute__((always_inline))
 init_one_static_tls (struct pthread *curp, struct link_map *map)
 {
-  dtv_t *dtv = GET_DTV (TLS_TPADJ (curp));
 # if TLS_TCB_AT_TP
   void *dest = (char *) curp - map->l_tls_offset;
 # elif TLS_DTV_AT_TP
@@ -1199,11 +1198,9 @@ init_one_static_tls (struct pthread *curp, struct link_map *map)
 #  error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
 # endif
 
-  /* Fill in the DTV slot so that a later LD/GD access will find it.  */
-  dtv[map->l_tls_modid].pointer.val = dest;
-  dtv[map->l_tls_modid].pointer.is_static = true;
-
-  /* Initialize the memory.  */
+  /* We cannot delay the initialization of the Static TLS area, since
+     it can be accessed with LE or IE, but since the DTV is only used
+     by GD and LD, we can delay its update to avoid a race.  */
   memset (__mempcpy (dest, map->l_tls_initimage, map->l_tls_initimage_size),
 	  '\0', map->l_tls_blocksize - map->l_tls_initimage_size);
 }
diff --git a/nptl_db/db-symbols.awk b/nptl_db/db-symbols.awk
index f9a91b9..eb089e1 100644
--- a/nptl_db/db-symbols.awk
+++ b/nptl_db/db-symbols.awk
@@ -2,6 +2,8 @@
 # we've just built.  It checks for all the symbols used in td_symbol_list.
 
 BEGIN {
+%define DB_RTLD_VARIABLE(name) /* Nothing. */
+%define DB_MAIN_VARIABLE(name) /* Nothing. */
 %define DB_LOOKUP_NAME(idx, name)		required[STRINGIFY (name)] = 1;
 %define DB_LOOKUP_NAME_TH_UNIQUE(idx, name)	th_unique[STRINGIFY (name)] = 1;
 %include "db-symbols.h"
diff --git a/nptl_db/db_info.c b/nptl_db/db_info.c
index d4a5438..b88b4c0 100644
--- a/nptl_db/db_info.c
+++ b/nptl_db/db_info.c
@@ -21,6 +21,7 @@
 #include <stdint.h>
 #include "thread_dbP.h"
 #include <tls.h>
+#include <ldsodefs.h>
 
 typedef struct pthread pthread;
 typedef struct pthread_key_struct pthread_key_struct;
@@ -37,6 +38,9 @@ typedef struct
 } dtv;
 
 typedef struct link_map link_map;
+typedef struct rtld_global rtld_global;
+typedef struct dtv_slotinfo_list dtv_slotinfo_list;
+typedef struct dtv_slotinfo dtv_slotinfo;
 
 /* Actually static in nptl/init.c, but we only need it for typeof.  */
 extern bool __nptl_initial_report_events;
diff --git a/nptl_db/fetch-value.c b/nptl_db/fetch-value.c
index afc26fc..8a5a30c 100644
--- a/nptl_db/fetch-value.c
+++ b/nptl_db/fetch-value.c
@@ -69,7 +69,8 @@ _td_locate_field (td_thragent_t *ta,
 	}
     }
 
-  if (idx != 0 && idx - (psaddr_t) 0 > DB_DESC_NELEM (desc))
+  if (idx != 0 && DB_DESC_NELEM (desc) != 0
+      && idx - (psaddr_t) 0 > DB_DESC_NELEM (desc))
     /* This is an internal indicator to callers with nonzero IDX
        that the IDX value is too big.  */
     return TD_NOAPLIC;
diff --git a/nptl_db/structs.def b/nptl_db/structs.def
index 42e8b4d..0d49a0a 100644
--- a/nptl_db/structs.def
+++ b/nptl_db/structs.def
@@ -22,6 +22,28 @@
 # define STRUCTS_DEF_DEFAULTS 1
 #endif
 
+#ifndef DB_RTLD_VARIABLE
+# define DB_RTLD_VARIABLE(name) DB_VARIABLE (name)
+#endif
+
+#ifndef DB_MAIN_VARIABLE
+# define DB_MAIN_VARIABLE(name) DB_VARIABLE (name)
+#endif
+
+#ifndef DB_RTLD_GLOBAL_FIELD
+# if !IS_IN (libpthread)
+#  define DB_RTLD_GLOBAL_FIELD(field)		\
+  DB_STRUCT_FIELD (rtld_global, _##field)	\
+  DB_MAIN_VARIABLE (_##field)
+# elif defined SHARED
+#  define DB_RTLD_GLOBAL_FIELD(field)		\
+  DB_STRUCT_FIELD (rtld_global, _##field)
+# else
+#  define DB_RTLD_GLOBAL_FIELD(field)		\
+  DB_MAIN_VARIABLE (_##field)
+# endif
+#endif /* DB_RTLD_GLOBAL_FIELD */
+
 DB_STRUCT (pthread)
 DB_STRUCT_FIELD (pthread, list)
 DB_STRUCT_FIELD (pthread, report_events)
@@ -70,14 +92,31 @@ DB_STRUCT (pthread_key_data_level2)
 DB_STRUCT_ARRAY_FIELD (pthread_key_data_level2, data)
 
 DB_STRUCT_FIELD (link_map, l_tls_modid)
+DB_STRUCT_FIELD (link_map, l_tls_offset)
 
 DB_STRUCT_ARRAY_FIELD (dtv, dtv)
 #define pointer_val pointer.val /* Field of anonymous struct in dtv_t.  */
 DB_STRUCT_FIELD (dtv_t, pointer_val)
+DB_STRUCT_FIELD (dtv_t, counter)
 #if !IS_IN (libpthread) || TLS_TCB_AT_TP
 DB_STRUCT_FIELD (pthread, dtvp)
 #endif
 
+#if !(IS_IN (libpthread) && !defined SHARED)
+DB_STRUCT (rtld_global)
+DB_RTLD_VARIABLE (_rtld_global)
+#endif
+DB_RTLD_GLOBAL_FIELD (dl_tls_dtv_slotinfo_list)
+
+DB_STRUCT (dtv_slotinfo_list)
+DB_STRUCT_FIELD (dtv_slotinfo_list, len)
+DB_STRUCT_FIELD (dtv_slotinfo_list, next)
+DB_STRUCT_ARRAY_FIELD (dtv_slotinfo_list, slotinfo)
+
+DB_STRUCT (dtv_slotinfo)
+DB_STRUCT_FIELD (dtv_slotinfo, gen)
+DB_STRUCT_FIELD (dtv_slotinfo, map)
+
 #ifdef STRUCTS_DEF_DEFAULTS
 # undef DB_STRUCT_ARRAY_FIELD
 # undef DB_ARRAY_VARIABLE
diff --git a/nptl_db/td_symbol_list.c b/nptl_db/td_symbol_list.c
index 6b14d36..7740e8c 100644
--- a/nptl_db/td_symbol_list.c
+++ b/nptl_db/td_symbol_list.c
@@ -18,7 +18,6 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <assert.h>
-#include <gnu/lib-names.h>
 #include "thread_dbP.h"
 
 static const char *symbol_list_arr[] =
@@ -41,12 +40,12 @@ td_symbol_list (void)
 
 
 ps_err_e
-td_lookup (struct ps_prochandle *ps, int idx, psaddr_t *sym_addr)
+td_mod_lookup (struct ps_prochandle *ps, const char *mod,
+	       int idx, psaddr_t *sym_addr)
 {
   ps_err_e result;
   assert (idx >= 0 && idx < SYM_NUM_MESSAGES);
-  result = ps_pglobal_lookup (ps, LIBPTHREAD_SO, symbol_list_arr[idx],
-			      sym_addr);
+  result = ps_pglobal_lookup (ps, mod, symbol_list_arr[idx], sym_addr);
 
 #ifdef HAVE_ASM_GLOBAL_DOT_NAME
   /* For PowerPC, 64-bit uses dot symbols but 32-bit does not.
diff --git a/nptl_db/td_thr_tlsbase.c b/nptl_db/td_thr_tlsbase.c
index 7092e31..24a489a 100644
--- a/nptl_db/td_thr_tlsbase.c
+++ b/nptl_db/td_thr_tlsbase.c
@@ -17,14 +17,118 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include "thread_dbP.h"
+#include <link.h>
 
+/* Get the DTV slotinfo list head entry from the dynamic loader state
+   into *LISTHEAD.  */
+static td_err_e
+dtv_slotinfo_list (td_thragent_t *ta,
+		   psaddr_t *listhead)
+{
+  td_err_e err;
+  psaddr_t head;
+
+  if (ta->ta_addr__rtld_global == 0
+      && td_mod_lookup (ta->ph, LD_SO, SYM__rtld_global,
+			&ta->ta_addr__rtld_global) != PS_OK)
+    ta->ta_addr__rtld_global = (void*)-1;
+
+  if (ta->ta_addr__rtld_global != (void*)-1)
+    {
+      err = DB_GET_FIELD (head, ta, ta->ta_addr__rtld_global,
+			  rtld_global, _dl_tls_dtv_slotinfo_list, 0);
+      if (err != TD_OK)
+	return err;
+    }
+  else
+    {
+      if (ta->ta_addr__dl_tls_dtv_slotinfo_list == 0
+	  && td_mod_lookup (ta->ph, NULL, SYM__dl_tls_dtv_slotinfo_list,
+			    &ta->ta_addr__dl_tls_dtv_slotinfo_list) != PS_OK)
+	return TD_ERR;
+
+      err = _td_fetch_value (ta, ta->ta_var__dl_tls_dtv_slotinfo_list,
+			     SYM_DESC__dl_tls_dtv_slotinfo_list,
+			     0, ta->ta_addr__dl_tls_dtv_slotinfo_list, &head);
+      if (err != TD_OK)
+	return err;
+    }
+
+  *listhead = head;
+  return TD_OK;
+}
+
+/* Get the address of the DTV slotinfo entry for MODID into
+   *DTVSLOTINFO.  */
+static td_err_e
+dtv_slotinfo (td_thragent_t *ta,
+	      unsigned long int modid,
+	      psaddr_t *dtvslotinfo)
+{
+  td_err_e err;
+  psaddr_t slot, temp;
+  size_t slbase = 0;
+
+  err = dtv_slotinfo_list (ta, &slot);
+  if (err != TD_OK)
+    return err;
+
+  while (slot)
+    {
+      /* Get the number of entries in this list entry's array.  */
+      err = DB_GET_FIELD (temp, ta, slot, dtv_slotinfo_list, len, 0);
+      if (err != TD_OK)
+	return err;
+      size_t len = (uintptr_t)temp;
+
+      /* Did we find the list entry for modid?  */
+      if (modid < slbase + len)
+	break;
+
+      /* We didn't, so get the next list entry.  */
+      slbase += len;
+      err = DB_GET_FIELD (temp, ta, slot, dtv_slotinfo_list,
+			  next, 0);
+      if (err != TD_OK)
+	return err;
+      slot = temp;
+    }
+
+  /* We reached the end of the list and found nothing.  */
+  if (!slot)
+    return TD_ERR;
+
+  /* Take the slotinfo for modid from the list entry.  */
+  err = DB_GET_FIELD_ADDRESS (temp, ta, slot, dtv_slotinfo_list,
+			      slotinfo, modid - slbase);
+  if (err != TD_OK)
+    return err;
+  slot = temp;
+
+  *dtvslotinfo = slot;
+  return TD_OK;
+}
+
+/* Return in *BASE the base address of the TLS block for MODID within
+   TH.
+
+   It should return success and yield the correct pointer in any
+   circumstance where the TLS block for the module and thread
+   requested has already been initialized.
+
+   It should fail with TD_TLSDEFER only when the thread could not
+   possibly have observed any values in that TLS block.  That way, the
+   debugger can fall back to showing initial values from the PT_TLS
+   segment (and refusing attempts to mutate) for the TD_TLSDEFER case,
+   and never fail to make the values the program will actually see
+   available to the user of the debugger.  */
 td_err_e
 td_thr_tlsbase (const td_thrhandle_t *th,
 		unsigned long int modid,
 		psaddr_t *base)
 {
   td_err_e err;
-  psaddr_t dtv, dtvslot, dtvptr;
+  psaddr_t dtv, dtvslot, dtvptr, temp;
 
   if (modid < 1)
     return TD_NOTLS;
@@ -50,11 +154,75 @@ td_thr_tlsbase (const td_thrhandle_t *th,
 	return TD_TLSDEFER;
     }
 
+  err = dtv_slotinfo (th->th_ta_p, modid, &temp);
+  if (err != TD_OK)
+    return err;
+
+  psaddr_t slot;
+  err = DB_GET_STRUCT (slot, th->th_ta_p, temp, dtv_slotinfo);
+  if (err != TD_OK)
+    return err;
+
+  /* Take the link_map from the slotinfo.  */
+  psaddr_t map;
+  err = DB_GET_FIELD_LOCAL (map, th->th_ta_p, slot, dtv_slotinfo, map, 0);
+  if (err != TD_OK)
+    return err;
+  if (!map)
+    return TD_ERR;
+
+  /* Ok, the modid is good, now find out what DTV generation it
+     requires.  */
+  err = DB_GET_FIELD_LOCAL (temp, th->th_ta_p, slot, dtv_slotinfo, gen, 0);
+  if (err != TD_OK)
+    return err;
+  size_t modgen = (uintptr_t)temp;
+
   /* Get the DTV pointer from the thread descriptor.  */
   err = DB_GET_FIELD (dtv, th->th_ta_p, pd, pthread, dtvp, 0);
   if (err != TD_OK)
     return err;
 
+  psaddr_t dtvgenloc;
+  /* Get the DTV generation count at dtv[0].counter.  */
+  err = DB_GET_FIELD_ADDRESS (dtvgenloc, th->th_ta_p, dtv, dtv, dtv, 0);
+  if (err != TD_OK)
+    return err;
+  err = DB_GET_FIELD (temp, th->th_ta_p, dtvgenloc, dtv_t, counter, 0);
+  if (err != TD_OK)
+    return err;
+  size_t dtvgen = (uintptr_t)temp;
+
+  /* Is the DTV current enough?  */
+  if (dtvgen < modgen)
+    {
+    try_static_tls:
+      /* If the module uses Static TLS, we're still good.  */
+      err = DB_GET_FIELD (temp, th->th_ta_p, map, link_map, l_tls_offset, 0);
+      if (err != TD_OK)
+	return err;
+      ptrdiff_t tlsoff = (uintptr_t)temp;
+
+      if (tlsoff != FORCED_DYNAMIC_TLS_OFFSET
+	  && tlsoff != NO_TLS_OFFSET)
+	{
+	  psaddr_t tp = pd;
+
+#if TLS_TCB_AT_TP
+	  dtvptr = tp - tlsoff;
+#elif TLS_DTV_AT_TP
+	  dtvptr = tp + tlsoff + TLS_PRE_TCB_SIZE;
+#else
+# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
+#endif
+
+	  *base = dtvptr;
+	  return TD_OK;
+	}
+
+      return TD_TLSDEFER;
+    }
+
   /* Find the corresponding entry in the DTV.  */
   err = DB_GET_FIELD_ADDRESS (dtvslot, th->th_ta_p, dtv, dtv, dtv, modid);
   if (err != TD_OK)
@@ -68,7 +236,7 @@ td_thr_tlsbase (const td_thrhandle_t *th,
   /* It could be that the memory for this module is not allocated for
      the given thread.  */
   if ((uintptr_t) dtvptr & 1)
-    return TD_TLSDEFER;
+    goto try_static_tls;
 
   *base = dtvptr;
   return TD_OK;
diff --git a/nptl_db/thread_dbP.h b/nptl_db/thread_dbP.h
index 4b59ce6..445c797 100644
--- a/nptl_db/thread_dbP.h
+++ b/nptl_db/thread_dbP.h
@@ -29,6 +29,7 @@
 #include "thread_db.h"
 #include "../nptl/pthreadP.h"  	/* This is for *_BITMASK only.  */
 #include <list.h>
+#include <gnu/lib-names.h>
 
 /* Indeces for the symbol names.  */
 enum
@@ -139,11 +140,11 @@ ta_ok (const td_thragent_t *ta)
 }
 
 
-/* Internal wrapper around ps_pglobal_lookup.  */
-extern ps_err_e td_lookup (struct ps_prochandle *ps,
-			   int idx, psaddr_t *sym_addr) attribute_hidden;
-
-
+/* Internal wrappers around ps_pglobal_lookup.  */
+extern ps_err_e td_mod_lookup (struct ps_prochandle *ps, const char *modname,
+			       int idx, psaddr_t *sym_addr) attribute_hidden;
+#define td_lookup(ps, idx, sym_addr) \
+  td_mod_lookup ((ps), LIBPTHREAD_SO, (idx), (sym_addr))
 
 
 /* Store in psaddr_t VAR the address of inferior's symbol NAME.  */

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=dd08634b37f81f01441d842087b123d12b4003d4

commit dd08634b37f81f01441d842087b123d12b4003d4
Author: Florian Weimer <fweimer@redhat.com>
Date:   Mon Jun 20 14:31:40 2016 +0200

    elf: Consolidate machine-agnostic DTV definitions in <dl-dtv.h>
    
    Identical definitions of dtv_t and TLS_DTV_UNALLOCATED were
    repeated for all architectures using DTVs.

diff --git a/ChangeLog b/ChangeLog
index 79fa583..3408f9c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,55 @@
+2016-06-20  Florian Weimer  <fweimer@redhat.com>
+
+	Consolidate machine-agnostic DTV definitions in <dl-dtv.h>.
+	* sysdeps/generic/dl-dtv.h: New file.
+	* sysdeps/aarch64/dl-tls.h (TLS_DTV_UNALLOCATED): Remove.
+	* sysdeps/aarch64/nptl/tls.h: Include <dl-dtv.h>.
+	(dtv_t): Remove.
+	* sysdeps/alpha/dl-tls.h (TLS_DTV_UNALLOCATED): Remove.
+	* sysdeps/alpha/nptl/tls.h: Include <dl-dtv.h>.
+	(dtv_t): Remove.
+	* sysdeps/arm/dl-tls.h (TLS_DTV_UNALLOCATED): Remove.
+	* sysdeps/arm/nptl/tls.h: Include <dl-dtv.h>.
+	(dtv_t): Remove.
+	* sysdeps/hppa/dl-tls.h (TLS_DTV_UNALLOCATED): Remove.
+	* sysdeps/hppa/nptl/tls.h: Include <dl-dtv.h>.
+	(dtv_t): Remove.
+	* sysdeps/i386/dl-tls.h (TLS_DTV_UNALLOCATED): Remove.
+	* sysdeps/i386/nptl/tls.h: Include <dl-dtv.h>.
+	(dtv_t): Remove.
+	* sysdeps/ia64/dl-tls.h (TLS_DTV_UNALLOCATED): Remove.
+	* sysdeps/ia64/nptl/tls.h: Include <dl-dtv.h>.
+	(dtv_t): Remove.
+	* sysdeps/m68k/dl-tls.h (TLS_DTV_UNALLOCATED): Remove.
+	* sysdeps/m68k/nptl/tls.h: Include <dl-dtv.h>.
+	(dtv_t): Remove.
+	* sysdeps/mach/hurd/i386/tls.h: Include <dl-dtv.h>.
+	(dtv_t): Remove.
+	* sysdeps/microblaze/dl-tls.h (TLS_DTV_UNALLOCATED): Remove.
+	* sysdeps/microblaze/nptl/tls.h: Include <dl-dtv.h>.
+	(dtv_t): Remove.
+	* sysdeps/mips/dl-tls.h (TLS_DTV_UNALLOCATED): Remove.
+	* sysdeps/mips/nptl/tls.h: Include <dl-dtv.h>.
+	(dtv_t): Remove.
+	* sysdeps/nios2/dl-tls.h (TLS_DTV_UNALLOCATED): Remove.
+	* sysdeps/nios2/nptl/tls.h: Include <dl-dtv.h>.
+	(dtv_t): Remove.
+	* sysdeps/powerpc/dl-tls.h (TLS_DTV_UNALLOCATED): Remove.
+	* sysdeps/powerpc/nptl/tls.h: Include <dl-dtv.h>.
+	(dtv_t): Remove.
+	* sysdeps/s390/dl-tls.h (TLS_DTV_UNALLOCATED): Remove.
+	* sysdeps/s390/nptl/tls.h: Include <dl-dtv.h>.
+	(dtv_t): Remove.
+	* sysdeps/sh/dl-tls.h (TLS_DTV_UNALLOCATED): Remove.
+	* sysdeps/sh/nptl/tls.h: Include <dl-dtv.h>.
+	(dtv_t): Remove.
+	* sysdeps/sparc/dl-tls.h (TLS_DTV_UNALLOCATED): Remove.
+	* sysdeps/sparc/nptl/tls.h: Include <dl-dtv.h>.
+	(dtv_t): Remove.
+	* sysdeps/x86_64/dl-tls.h (TLS_DTV_UNALLOCATED): Remove.
+	* sysdeps/x86_64/nptl/tls.h: Include <dl-dtv.h>.
+	(dtv_t): Remove.
+
 2016-04-14  Florian Weimer  <fweimer@redhat.com>
 
 	[BZ #19431]
diff --git a/sysdeps/aarch64/dl-tls.h b/sysdeps/aarch64/dl-tls.h
index 2465716..9a7d716 100644
--- a/sysdeps/aarch64/dl-tls.h
+++ b/sysdeps/aarch64/dl-tls.h
@@ -25,6 +25,3 @@ typedef struct
 
 
 extern void *__tls_get_addr (tls_index *ti);
-
-/* Value used for dtv entries for which the allocation is delayed.  */
-#define TLS_DTV_UNALLOCATED ((void *) -1l)
diff --git a/sysdeps/aarch64/nptl/tls.h b/sysdeps/aarch64/nptl/tls.h
index 1e3904e..10b82cf 100644
--- a/sysdeps/aarch64/nptl/tls.h
+++ b/sysdeps/aarch64/nptl/tls.h
@@ -25,17 +25,7 @@
 # include <stdbool.h>
 # include <stddef.h>
 # include <stdint.h>
-
-/* Type for the dtv.  */
-typedef union dtv
-{
-  size_t counter;
-  struct
-  {
-    void *val;
-    bool is_static;
-  } pointer;
-} dtv_t;
+# include <dl-dtv.h>
 
 #else /* __ASSEMBLER__ */
 # include <tcb-offsets.h>
diff --git a/sysdeps/alpha/dl-tls.h b/sysdeps/alpha/dl-tls.h
index 5cdd439..eb0126c 100644
--- a/sysdeps/alpha/dl-tls.h
+++ b/sysdeps/alpha/dl-tls.h
@@ -25,6 +25,3 @@ typedef struct
 } tls_index;
 
 extern void *__tls_get_addr (tls_index *ti);
-
-/* Value used for dtv entries for which the allocation is delayed.  */
-#define TLS_DTV_UNALLOCATED	((void *) -1l)
diff --git a/sysdeps/alpha/nptl/tls.h b/sysdeps/alpha/nptl/tls.h
index 4eddf40..2b360e9 100644
--- a/sysdeps/alpha/nptl/tls.h
+++ b/sysdeps/alpha/nptl/tls.h
@@ -25,17 +25,7 @@
 # include <stdbool.h>
 # include <stddef.h>
 # include <stdint.h>
-
-/* Type for the dtv.  */
-typedef union dtv
-{
-  size_t counter;
-  struct
-  {
-    void *val;
-    bool is_static;
-  } pointer;
-} dtv_t;
+# include <dl-dtv.h>
 
 /* Get system call information.  */
 # include <sysdep.h>
diff --git a/sysdeps/arm/dl-tls.h b/sysdeps/arm/dl-tls.h
index 30599bb..0e716e5 100644
--- a/sysdeps/arm/dl-tls.h
+++ b/sysdeps/arm/dl-tls.h
@@ -26,6 +26,3 @@ typedef struct dl_tls_index
 
 
 extern void *__tls_get_addr (tls_index *ti);
-
-/* Value used for dtv entries for which the allocation is delayed.  */
-#define TLS_DTV_UNALLOCATED	((void *) -1l)
diff --git a/sysdeps/arm/nptl/tls.h b/sysdeps/arm/nptl/tls.h
index 27ea006..f69ed1b 100644
--- a/sysdeps/arm/nptl/tls.h
+++ b/sysdeps/arm/nptl/tls.h
@@ -25,17 +25,7 @@
 # include <stdbool.h>
 # include <stddef.h>
 # include <stdint.h>
-
-/* Type for the dtv.  */
-typedef union dtv
-{
-  size_t counter;
-  struct
-  {
-    void *val;
-    bool is_static;
-  } pointer;
-} dtv_t;
+# include <dl-dtv.h>
 
 #else /* __ASSEMBLER__ */
 # include <tcb-offsets.h>
diff --git a/sysdeps/sh/dl-tls.h b/sysdeps/generic/dl-dtv.h
similarity index 67%
copy from sysdeps/sh/dl-tls.h
copy to sysdeps/generic/dl-dtv.h
index 2b6d573..36c5c58 100644
--- a/sysdeps/sh/dl-tls.h
+++ b/sysdeps/generic/dl-dtv.h
@@ -1,5 +1,5 @@
-/* Thread-local storage handling in the ELF dynamic linker.  SH version.
-   Copyright (C) 2002-2015 Free Software Foundation, Inc.
+/* Generic declarations for DTV-based TLS handling in the dynamic linker.
+   Copyright (C) 2002-2016 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
@@ -16,16 +16,21 @@
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
+#ifndef _DL_DTV_H
+#define _DL_DTV_H
 
-/* Type used for the representation of TLS information in the GOT.  */
-typedef struct
+/* Type for the dtv.  */
+typedef union dtv
 {
-  unsigned long int ti_module;
-  unsigned long int ti_offset;
-} tls_index;
-
-
-extern void *__tls_get_addr (tls_index *ti);
+  size_t counter;
+  struct
+  {
+    void *val;
+    bool is_static;
+  } pointer;
+} dtv_t;
 
 /* Value used for dtv entries for which the allocation is delayed.  */
-#define TLS_DTV_UNALLOCATED	((void *) -1l)
+#define TLS_DTV_UNALLOCATED ((void *) -1l)
+
+#endif /* _DLT_DTV_H */
diff --git a/sysdeps/hppa/dl-tls.h b/sysdeps/hppa/dl-tls.h
index bf0b2a4..e9eda9f 100644
--- a/sysdeps/hppa/dl-tls.h
+++ b/sysdeps/hppa/dl-tls.h
@@ -26,6 +26,3 @@ typedef struct
 
 
 extern void *__tls_get_addr (tls_index *ti);
-
-/* Value used for dtv entries for which the allocation is delayed.  */
-#define TLS_DTV_UNALLOCATED	((void *) -1l)
diff --git a/sysdeps/hppa/nptl/tls.h b/sysdeps/hppa/nptl/tls.h
index ab271cf..5ac6e63 100644
--- a/sysdeps/hppa/nptl/tls.h
+++ b/sysdeps/hppa/nptl/tls.h
@@ -25,17 +25,7 @@
 # include <stdbool.h>
 # include <stddef.h>
 # include <stdint.h>
-
-/* Type for the dtv.  */
-typedef union dtv
-{
-  size_t counter;
-  struct
-  {
-    void *val;
-    bool is_static;
-  } pointer;
-} dtv_t;
+# include <dl-dtv.h>
 
 #else /* __ASSEMBLER__ */
 # include <tcb-offsets.h>
diff --git a/sysdeps/i386/dl-tls.h b/sysdeps/i386/dl-tls.h
index f8e108f..9072327 100644
--- a/sysdeps/i386/dl-tls.h
+++ b/sysdeps/i386/dl-tls.h
@@ -59,6 +59,3 @@ rtld_hidden_def (___tls_get_addr)
 
 # endif
 #endif
-
-/* Value used for dtv entries for which the allocation is delayed.  */
-#define TLS_DTV_UNALLOCATED	((void *) -1l)
diff --git a/sysdeps/i386/nptl/tls.h b/sysdeps/i386/nptl/tls.h
index 40f84ef..5f6c3d7 100644
--- a/sysdeps/i386/nptl/tls.h
+++ b/sysdeps/i386/nptl/tls.h
@@ -28,19 +28,7 @@
 # include <sysdep.h>
 # include <libc-internal.h>
 # include <kernel-features.h>
-
-
-/* Type for the dtv.  */
-typedef union dtv
-{
-  size_t counter;
-  struct
-  {
-    void *val;
-    bool is_static;
-  } pointer;
-} dtv_t;
-
+# include <dl-dtv.h>
 
 typedef struct
 {
diff --git a/sysdeps/ia64/dl-tls.h b/sysdeps/ia64/dl-tls.h
index a36d990..e9bb0ca 100644
--- a/sysdeps/ia64/dl-tls.h
+++ b/sysdeps/ia64/dl-tls.h
@@ -28,6 +28,3 @@
 #define DONT_USE_TLS_INDEX	1
 
 extern void *__tls_get_addr (size_t m, size_t offset);
-
-/* Value used for dtv entries for which the allocation is delayed.  */
-#define TLS_DTV_UNALLOCATED	((void *) -1l)
diff --git a/sysdeps/ia64/nptl/tls.h b/sysdeps/ia64/nptl/tls.h
index 1668fd4..65226d7 100644
--- a/sysdeps/ia64/nptl/tls.h
+++ b/sysdeps/ia64/nptl/tls.h
@@ -26,19 +26,7 @@
 # include <stdint.h>
 # include <stdlib.h>
 # include <list.h>
-
-
-/* Type for the dtv.  */
-typedef union dtv
-{
-  size_t counter;
-  struct
-  {
-    void *val;
-    bool is_static;
-  } pointer;
-} dtv_t;
-
+# include <dl-dtv.h>
 
 typedef struct
 {
diff --git a/sysdeps/m68k/dl-tls.h b/sysdeps/m68k/dl-tls.h
index 82ff78c..fab9f0a 100644
--- a/sysdeps/m68k/dl-tls.h
+++ b/sysdeps/m68k/dl-tls.h
@@ -45,6 +45,3 @@ extern void *__tls_get_addr (tls_index *ti);
 
 #define GET_ADDR_OFFSET		(ti->ti_offset + TLS_DTV_OFFSET)
 #define __TLS_GET_ADDR(__ti)	(__tls_get_addr (__ti) - TLS_DTV_OFFSET)
-
-/* Value used for dtv entries for which the allocation is delayed.  */
-#define TLS_DTV_UNALLOCATED	((void *) -1l)
diff --git a/sysdeps/m68k/nptl/tls.h b/sysdeps/m68k/nptl/tls.h
index 93bc1ad..703d368 100644
--- a/sysdeps/m68k/nptl/tls.h
+++ b/sysdeps/m68k/nptl/tls.h
@@ -26,17 +26,7 @@
 # include <stdbool.h>
 # include <stddef.h>
 # include <stdint.h>
-
-/* Type for the dtv.  */
-typedef union dtv
-{
-  size_t counter;
-  struct
-  {
-    void *val;
-    bool is_static;
-  } pointer;
-} dtv_t;
+# include <dl-dtv.h>
 
 #else /* __ASSEMBLER__ */
 # include <tcb-offsets.h>
diff --git a/sysdeps/mach/hurd/i386/tls.h b/sysdeps/mach/hurd/i386/tls.h
index ca68d1c..34564ae 100644
--- a/sysdeps/mach/hurd/i386/tls.h
+++ b/sysdeps/mach/hurd/i386/tls.h
@@ -25,17 +25,7 @@
 
 
 #ifndef __ASSEMBLER__
-/* Type for the dtv.  */
-typedef union dtv
-{
-  size_t counter;
-  struct
-  {
-    void *val;
-    bool is_static;
-  } pointer;
-} dtv_t;
-
+# include <dl-dtv.h>
 
 /* Type of the TCB.  */
 typedef struct
diff --git a/sysdeps/microblaze/dl-tls.h b/sysdeps/microblaze/dl-tls.h
index 704ad25..9f0f491 100644
--- a/sysdeps/microblaze/dl-tls.h
+++ b/sysdeps/microblaze/dl-tls.h
@@ -24,6 +24,3 @@ typedef struct
 } tls_index;
 
 extern void *__tls_get_addr (tls_index *ti);
-
-/* Value used for dtv entries for which the allocation is delayed.  */
-#define TLS_DTV_UNALLOCATED	((void *) -1l)
diff --git a/sysdeps/microblaze/nptl/tls.h b/sysdeps/microblaze/nptl/tls.h
index 171a745..42d2c4b 100644
--- a/sysdeps/microblaze/nptl/tls.h
+++ b/sysdeps/microblaze/nptl/tls.h
@@ -25,17 +25,7 @@
 # include <stdbool.h>
 # include <stddef.h>
 # include <stdint.h>
-
-/* Type for the dtv.  */
-typedef union dtv
-{
-  size_t counter;
-  struct
-  {
-    void *val;
-    bool is_static;
-  } pointer;
-} dtv_t;
+# include <dl-dtv.h>
 
 #else /* __ASSEMBLER__ */
 # include <tcb-offsets.h>
diff --git a/sysdeps/mips/dl-tls.h b/sysdeps/mips/dl-tls.h
index 54dc711..58d968f 100644
--- a/sysdeps/mips/dl-tls.h
+++ b/sysdeps/mips/dl-tls.h
@@ -43,6 +43,3 @@ extern void *__tls_get_addr (tls_index *ti);
 
 # define GET_ADDR_OFFSET	(ti->ti_offset + TLS_DTV_OFFSET)
 # define __TLS_GET_ADDR(__ti)	(__tls_get_addr (__ti) - TLS_DTV_OFFSET)
-
-/* Value used for dtv entries for which the allocation is delayed.  */
-#define TLS_DTV_UNALLOCATED	((void *) -1l)
diff --git a/sysdeps/mips/nptl/tls.h b/sysdeps/mips/nptl/tls.h
index ef11457..95f8d33 100644
--- a/sysdeps/mips/nptl/tls.h
+++ b/sysdeps/mips/nptl/tls.h
@@ -25,17 +25,10 @@
 # include <stdbool.h>
 # include <stddef.h>
 # include <stdint.h>
+# include <dl-dtv.h>
 
-/* Type for the dtv.  */
-typedef union dtv
-{
-  size_t counter;
-  struct
-  {
-    void *val;
-    bool is_static;
-  } pointer;
-} dtv_t;
+/* Get system call information.  */
+# include <sysdep.h>
 
 #ifdef __mips16
 /* MIPS16 uses GCC builtin to access the TP.  */
diff --git a/sysdeps/nios2/dl-tls.h b/sysdeps/nios2/dl-tls.h
index 541e4b0..65a9cc8 100644
--- a/sysdeps/nios2/dl-tls.h
+++ b/sysdeps/nios2/dl-tls.h
@@ -43,6 +43,3 @@ extern void *__tls_get_addr (tls_index *ti);
 
 # define GET_ADDR_OFFSET	(ti->ti_offset + TLS_DTV_OFFSET)
 # define __TLS_GET_ADDR(__ti)	(__tls_get_addr (__ti) - TLS_DTV_OFFSET)
-
-/* Value used for dtv entries for which the allocation is delayed.  */
-#define TLS_DTV_UNALLOCATED	((void *) -1l)
diff --git a/sysdeps/nios2/nptl/tls.h b/sysdeps/nios2/nptl/tls.h
index 465a4b9..3fd8b32 100644
--- a/sysdeps/nios2/nptl/tls.h
+++ b/sysdeps/nios2/nptl/tls.h
@@ -25,17 +25,7 @@
 # include <stdbool.h>
 # include <stddef.h>
 # include <stdint.h>
-
-/* Type for the dtv.  */
-typedef union dtv
-{
-  size_t counter;
-  struct
-  {
-    void *val;
-    bool is_static;
-  } pointer;
-} dtv_t;
+# include <dl-dtv.h>
 
 #else /* __ASSEMBLER__ */
 # include <tcb-offsets.h>
diff --git a/sysdeps/powerpc/dl-tls.h b/sysdeps/powerpc/dl-tls.h
index 4e3e93a..cc0771a 100644
--- a/sysdeps/powerpc/dl-tls.h
+++ b/sysdeps/powerpc/dl-tls.h
@@ -49,7 +49,4 @@ extern void *__tls_get_addr (tls_index *ti);
 # define __TLS_GET_ADDR(__ti)	(__tls_get_addr (__ti) - TLS_DTV_OFFSET)
 #endif
 
-/* Value used for dtv entries for which the allocation is delayed.  */
-#define TLS_DTV_UNALLOCATED	((void *) -1l)
-
 #endif /* dl-tls.h */
diff --git a/sysdeps/powerpc/nptl/tls.h b/sysdeps/powerpc/nptl/tls.h
index 1f3d97a..93d0011 100644
--- a/sysdeps/powerpc/nptl/tls.h
+++ b/sysdeps/powerpc/nptl/tls.h
@@ -25,17 +25,7 @@
 # include <stdbool.h>
 # include <stddef.h>
 # include <stdint.h>
-
-/* Type for the dtv.  */
-typedef union dtv
-{
-  size_t counter;
-  struct
-  {
-    void *val;
-    bool is_static;
-  } pointer;
-} dtv_t;
+# include <dl-dtv.h>
 
 #else /* __ASSEMBLER__ */
 # include <tcb-offsets.h>
diff --git a/sysdeps/s390/dl-tls.h b/sysdeps/s390/dl-tls.h
index 8132b10..98262b2 100644
--- a/sysdeps/s390/dl-tls.h
+++ b/sysdeps/s390/dl-tls.h
@@ -102,6 +102,3 @@ extern void *__tls_get_addr_internal (tls_index *ti);
       + (unsigned long) __builtin_thread_pointer (); })
 
 #endif
-
-/* Value used for dtv entries for which the allocation is delayed.  */
-#define TLS_DTV_UNALLOCATED	((void *) -1l)
diff --git a/sysdeps/s390/nptl/tls.h b/sysdeps/s390/nptl/tls.h
index e6f8a47..4a5638e 100644
--- a/sysdeps/s390/nptl/tls.h
+++ b/sysdeps/s390/nptl/tls.h
@@ -27,19 +27,7 @@
 # include <stdlib.h>
 # include <list.h>
 # include <kernel-features.h>
-
-
-/* Type for the dtv.  */
-typedef union dtv
-{
-  size_t counter;
-  struct
-  {
-    void *val;
-    bool is_static;
-  } pointer;
-} dtv_t;
-
+# include <dl-dtv.h>
 
 typedef struct
 {
diff --git a/sysdeps/sh/dl-tls.h b/sysdeps/sh/dl-tls.h
index 2b6d573..2b2bbab 100644
--- a/sysdeps/sh/dl-tls.h
+++ b/sysdeps/sh/dl-tls.h
@@ -26,6 +26,3 @@ typedef struct
 
 
 extern void *__tls_get_addr (tls_index *ti);
-
-/* Value used for dtv entries for which the allocation is delayed.  */
-#define TLS_DTV_UNALLOCATED	((void *) -1l)
diff --git a/sysdeps/sh/nptl/tls.h b/sysdeps/sh/nptl/tls.h
index 9615a76..4421910 100644
--- a/sysdeps/sh/nptl/tls.h
+++ b/sysdeps/sh/nptl/tls.h
@@ -28,17 +28,7 @@
 # include <stdlib.h>
 # include <list.h>
 # include <sysdep.h>
-
-/* Type for the dtv.  */
-typedef union dtv
-{
-  size_t counter;
-  struct
-  {
-    void *val;
-    bool is_static;
-  } pointer;
-} dtv_t;
+# include <dl-dtv.h>
 
 typedef struct
 {
diff --git a/sysdeps/sparc/dl-tls.h b/sysdeps/sparc/dl-tls.h
index 9fbe909..0c0ca06 100644
--- a/sysdeps/sparc/dl-tls.h
+++ b/sysdeps/sparc/dl-tls.h
@@ -26,6 +26,3 @@ typedef struct
 
 
 extern void *__tls_get_addr (tls_index *ti);
-
-/* Value used for dtv entries for which the allocation is delayed.  */
-#define TLS_DTV_UNALLOCATED	((void *) -1l)
diff --git a/sysdeps/sparc/nptl/tls.h b/sysdeps/sparc/nptl/tls.h
index 54bce7e..9c0c515 100644
--- a/sysdeps/sparc/nptl/tls.h
+++ b/sysdeps/sparc/nptl/tls.h
@@ -27,17 +27,7 @@
 # include <stdlib.h>
 # include <list.h>
 # include <kernel-features.h>
-
-/* Type for the dtv.  */
-typedef union dtv
-{
-  size_t counter;
-  struct
-  {
-    void *val;
-    bool is_static;
-  } pointer;
-} dtv_t;
+# include <dl-dtv.h>
 
 typedef struct
 {
diff --git a/sysdeps/tile/dl-tls.h b/sysdeps/tile/dl-tls.h
index 16c5a43..b66cc08 100644
--- a/sysdeps/tile/dl-tls.h
+++ b/sysdeps/tile/dl-tls.h
@@ -40,6 +40,3 @@ extern void *__tls_get_addr (tls_index *ti);
 /* Compute the value for a DTPREL reloc.  */
 #define TLS_DTPREL_VALUE(sym) \
   ((sym)->st_value - TLS_DTV_OFFSET)
-
-/* Value used for dtv entries for which the allocation is delayed.  */
-#define TLS_DTV_UNALLOCATED    ((void *) -1l)
diff --git a/sysdeps/tile/nptl/tls.h b/sysdeps/tile/nptl/tls.h
index 08e1d54..9ab195f 100644
--- a/sysdeps/tile/nptl/tls.h
+++ b/sysdeps/tile/nptl/tls.h
@@ -25,17 +25,7 @@
 # include <stdbool.h>
 # include <stddef.h>
 # include <stdint.h>
-
-/* Type for the dtv.  */
-typedef union dtv
-{
-  size_t counter;
-  struct
-  {
-    void *val;
-    bool is_static;
-  } pointer;
-} dtv_t;
+# include <dl-dtv.h>
 
 #else /* __ASSEMBLER__ */
 # include <tcb-offsets.h>
diff --git a/sysdeps/x86_64/dl-tls.h b/sysdeps/x86_64/dl-tls.h
index 285799b..eba9993 100644
--- a/sysdeps/x86_64/dl-tls.h
+++ b/sysdeps/x86_64/dl-tls.h
@@ -27,6 +27,3 @@ typedef struct dl_tls_index
 
 
 extern void *__tls_get_addr (tls_index *ti);
-
-/* Value used for dtv entries for which the allocation is delayed.  */
-#define TLS_DTV_UNALLOCATED	((void *) -1l)
diff --git a/sysdeps/x86_64/nptl/tls.h b/sysdeps/x86_64/nptl/tls.h
index b947d57..0672348 100644
--- a/sysdeps/x86_64/nptl/tls.h
+++ b/sysdeps/x86_64/nptl/tls.h
@@ -28,6 +28,7 @@
 # include <sysdep.h>
 # include <libc-internal.h>
 # include <kernel-features.h>
+# include <dl-dtv.h>
 
 /* Replacement type for __m128 since this file is included by ld.so,
    which is compiled with -mno-sse.  It must not change the alignment
@@ -38,18 +39,6 @@ typedef struct
 } __128bits;
 
 
-/* Type for the dtv.  */
-typedef union dtv
-{
-  size_t counter;
-  struct
-  {
-    void *val;
-    bool is_static;
-  } pointer;
-} dtv_t;
-
-
 typedef struct
 {
   void *tcb;		/* Pointer to the TCB.  Not necessarily the

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=749c94d44c9aa063dfc7ad2b505c8e6d35e57345

commit 749c94d44c9aa063dfc7ad2b505c8e6d35e57345
Author: Florian Weimer <fweimer@redhat.com>
Date:   Thu Apr 14 09:17:02 2016 +0200

    malloc: Run fork handler as late as possible [BZ #19431]
    
    Previously, a thread M invoking fork would acquire locks in this order:
    
      (M1) malloc arena locks (in the registered fork handler)
      (M2) libio list lock
    
    A thread F invoking flush (NULL) would acquire locks in this order:
    
      (F1) libio list lock
      (F2) individual _IO_FILE locks
    
    A thread G running getdelim would use this order:
    
      (G1) _IO_FILE lock
      (G2) malloc arena lock
    
    After executing (M1), (F1), (G1), none of the threads can make progress.
    
    This commit changes the fork lock order to:
    
      (M'1) libio list lock
      (M'2) malloc arena locks
    
    It explicitly encodes the lock order in the implementations of fork,
    and does not rely on the registration order, thus avoiding the deadlock.

diff --git a/ChangeLog b/ChangeLog
index 4601726..79fa583 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,27 @@
+2016-04-14  Florian Weimer  <fweimer@redhat.com>
+
+	[BZ #19431]
+	Run the malloc fork handler as late as possible to avoid deadlocks.
+	* malloc/malloc-internal.h: New file.
+	* malloc/malloc.c: Include it.
+	* malloc/arena.c (ATFORK_MEM): Remove.
+	(__malloc_fork_lock_parent): Rename from ptmalloc_lock_all.
+	Update comment.
+	(__malloc_fork_unlock_parent): Rename from ptmalloc_unlock_all.
+	(__malloc_fork_unlock_child): Rename from ptmalloc_unlock_all2.
+	Remove outdated comment.
+	(ptmalloc_init): Do not call thread_atfork.  Remove
+	thread_atfork_static.
+	* malloc/tst-malloc-fork-deadlock.c: New file.
+	* Makefile (tests): Add tst-malloc-fork-deadlock.
+	(tst-malloc-fork-deadlock): Link against libpthread.
+	* manual/memory.texi (Aligned Memory Blocks): Update safety
+	annotation comments.
+	* sysdeps/nptl/fork.c (__libc_fork): Call
+	__malloc_fork_lock_parent, __malloc_fork_unlock_parent,
+	__malloc_fork_unlock_child.
+	* sysdeps/mach/hurd/fork.c (__fork): Likewise.
+
 2015-12-21  Florian Weimer  <fweimer@redhat.com>
 
 	[BZ #19182]
diff --git a/malloc/Makefile b/malloc/Makefile
index 5f68a79..071f917 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -27,7 +27,7 @@ headers := $(dist-headers) obstack.h mcheck.h
 tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \
 	 tst-mallocstate tst-mcheck tst-mallocfork tst-trim1 \
 	 tst-malloc-usable tst-realloc tst-posix_memalign \
-	 tst-pvalloc tst-memalign tst-mallopt
+	 tst-pvalloc tst-memalign tst-mallopt tst-malloc-fork-deadlock
 test-srcs = tst-mtrace
 
 routines = malloc morecore mcheck mtrace obstack
@@ -42,6 +42,8 @@ extra-libs-others = $(extra-libs)
 libmemusage-routines = memusage
 libmemusage-inhibit-o = $(filter-out .os,$(object-suffixes))
 
+$(objpfx)tst-malloc-fork-deadlock: $(shared-thread-library)
+
 # These should be removed by `make clean'.
 extra-objs = mcheck-init.o libmcheck.a
 
diff --git a/malloc/arena.c b/malloc/arena.c
index 6ceb9df..921833d 100644
--- a/malloc/arena.c
+++ b/malloc/arena.c
@@ -137,10 +137,6 @@ static void *(*save_malloc_hook)(size_t __size, const void *);
 static void (*save_free_hook) (void *__ptr, const void *);
 static void *save_arena;
 
-# ifdef ATFORK_MEM
-ATFORK_MEM;
-# endif
-
 /* Magic value for the thread-specific arena pointer when
    malloc_atfork() is in use.  */
 
@@ -206,14 +202,14 @@ free_atfork (void *mem, const void *caller)
 /* Counter for number of times the list is locked by the same thread.  */
 static unsigned int atfork_recursive_cntr;
 
-/* The following two functions are registered via thread_atfork() to
-   make sure that the mutexes remain in a consistent state in the
-   fork()ed version of a thread.  Also adapt the malloc and free hooks
-   temporarily, because the `atfork' handler mechanism may use
-   malloc/free internally (e.g. in LinuxThreads). */
+/* The following three functions are called around fork from a
+   multi-threaded process.  We do not use the general fork handler
+   mechanism to make sure that our handlers are the last ones being
+   called, so that other fork handlers can use the malloc
+   subsystem.  */
 
-static void
-ptmalloc_lock_all (void)
+void
+__malloc_fork_lock_parent (void)
 {
   mstate ar_ptr;
 
@@ -221,7 +217,7 @@ ptmalloc_lock_all (void)
     return;
 
   /* We do not acquire free_list_lock here because we completely
-     reconstruct free_list in ptmalloc_unlock_all2.  */
+     reconstruct free_list in __malloc_fork_unlock_child.  */
 
   if (mutex_trylock (&list_lock))
     {
@@ -246,7 +242,7 @@ ptmalloc_lock_all (void)
   __free_hook = free_atfork;
   /* Only the current thread may perform malloc/free calls now.
      save_arena will be reattached to the current thread, in
-     ptmalloc_lock_all, so save_arena->attached_threads is not
+     __malloc_fork_lock_parent, so save_arena->attached_threads is not
      updated.  */
   save_arena = thread_arena;
   thread_arena = ATFORK_ARENA_PTR;
@@ -254,8 +250,8 @@ out:
   ++atfork_recursive_cntr;
 }
 
-static void
-ptmalloc_unlock_all (void)
+void
+__malloc_fork_unlock_parent (void)
 {
   mstate ar_ptr;
 
@@ -266,8 +262,8 @@ ptmalloc_unlock_all (void)
     return;
 
   /* Replace ATFORK_ARENA_PTR with save_arena.
-     save_arena->attached_threads was not changed in ptmalloc_lock_all
-     and is still correct.  */
+     save_arena->attached_threads was not changed in
+     __malloc_fork_lock_parent and is still correct.  */
   thread_arena = save_arena;
   __malloc_hook = save_malloc_hook;
   __free_hook = save_free_hook;
@@ -281,15 +277,8 @@ ptmalloc_unlock_all (void)
   (void) mutex_unlock (&list_lock);
 }
 
-# ifdef __linux__
-
-/* In NPTL, unlocking a mutex in the child process after a
-   fork() is currently unsafe, whereas re-initializing it is safe and
-   does not leak resources.  Therefore, a special atfork handler is
-   installed for the child. */
-
-static void
-ptmalloc_unlock_all2 (void)
+void
+__malloc_fork_unlock_child (void)
 {
   mstate ar_ptr;
 
@@ -328,8 +317,8 @@ ptmalloc_unlock_all2 (void)
 # else
 
 #  define ptmalloc_unlock_all2 ptmalloc_unlock_all
-# endif
-#endif  /* !NO_THREADS */
+#endif
+
 
 /* Initialization routine. */
 #include <string.h>
@@ -399,7 +388,6 @@ ptmalloc_init (void)
 #endif
 
   thread_arena = &main_arena;
-  thread_atfork (ptmalloc_lock_all, ptmalloc_unlock_all, ptmalloc_unlock_all2);
   const char *s = NULL;
   if (__glibc_likely (_environ != NULL))
     {
@@ -474,14 +462,6 @@ ptmalloc_init (void)
   __malloc_initialized = 1;
 }
 
-/* There are platforms (e.g. Hurd) with a link-time hook mechanism. */
-#ifdef thread_atfork_static
-thread_atfork_static (ptmalloc_lock_all, ptmalloc_unlock_all,		      \
-                      ptmalloc_unlock_all2)
-#endif
-
-
-
 /* Managing heaps and arenas (for concurrent threads) */
 
 #if MALLOC_DEBUG > 1
@@ -817,7 +797,8 @@ _int_new_arena (size_t size)
      limit is reached).  At this point, some arena has to be attached
      to two threads.  We could acquire the arena lock before list_lock
      to make it less likely that reused_arena picks this new arena,
-     but this could result in a deadlock with ptmalloc_lock_all.  */
+     but this could result in a deadlock with
+     __malloc_fork_lock_parent.  */
 
   (void) mutex_lock (&a->mutex);
 
diff --git a/malloc/malloc-internal.h b/malloc/malloc-internal.h
new file mode 100644
index 0000000..b830d3f
--- /dev/null
+++ b/malloc/malloc-internal.h
@@ -0,0 +1,32 @@
+/* Internal declarations for malloc, for use within libc.
+   Copyright (C) 2016 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; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef _MALLOC_PRIVATE_H
+#define _MALLOC_PRIVATE_H
+
+/* Called in the parent process before a fork.  */
+void __malloc_fork_lock_parent (void) internal_function attribute_hidden;
+
+/* Called in the parent process after a fork.  */
+void __malloc_fork_unlock_parent (void) internal_function attribute_hidden;
+
+/* Called in the child process after a fork.  */
+void __malloc_fork_unlock_child (void) internal_function attribute_hidden;
+
+
+#endif /* _MALLOC_PRIVATE_H */
diff --git a/malloc/malloc.c b/malloc/malloc.c
index f190309..a113b6e 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -241,6 +241,10 @@
 /* For MIN, MAX, powerof2.  */
 #include <sys/param.h>
 
+/* For ALIGN_UP et. al.  */
+#include <libc-internal.h>
+
+#include <malloc/malloc-internal.h>
 
 /*
   Debugging:
diff --git a/malloc/tst-malloc-fork-deadlock.c b/malloc/tst-malloc-fork-deadlock.c
new file mode 100644
index 0000000..94549ca
--- /dev/null
+++ b/malloc/tst-malloc-fork-deadlock.c
@@ -0,0 +1,220 @@
+/* Test concurrent fork, getline, and fflush (NULL).
+   Copyright (C) 2016 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; see the file COPYING.LIB.  If
+   not, see <http://www.gnu.org/licenses/>.  */
+
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <time.h>
+#include <string.h>
+#include <signal.h>
+
+static int do_test (void);
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
+
+enum {
+  /* Number of threads which call fork.  */
+  fork_thread_count = 4,
+  /* Number of threads which call getline (and, indirectly,
+     malloc).  */
+  read_thread_count = 8,
+};
+
+static bool termination_requested;
+
+static void *
+fork_thread_function (void *closure)
+{
+  while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
+    {
+      pid_t pid = fork ();
+      if (pid < 0)
+        {
+          printf ("error: fork: %m\n");
+          abort ();
+        }
+      else if (pid == 0)
+        _exit (17);
+
+      int status;
+      if (waitpid (pid, &status, 0) < 0)
+        {
+          printf ("error: waitpid: %m\n");
+          abort ();
+        }
+      if (!WIFEXITED (status) || WEXITSTATUS (status) != 17)
+        {
+          printf ("error: waitpid returned invalid status: %d\n", status);
+          abort ();
+        }
+    }
+  return NULL;
+}
+
+static char *file_to_read;
+
+static void *
+read_thread_function (void *closure)
+{
+  FILE *f = fopen (file_to_read, "r");
+  if (f == NULL)
+    {
+      printf ("error: fopen (%s): %m\n", file_to_read);
+      abort ();
+    }
+
+  while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
+    {
+      rewind (f);
+      char *line = NULL;
+      size_t line_allocated = 0;
+      ssize_t ret = getline (&line, &line_allocated, f);
+      if (ret < 0)
+        {
+          printf ("error: getline: %m\n");
+          abort ();
+        }
+      free (line);
+    }
+  fclose (f);
+
+  return NULL;
+}
+
+static void *
+flushall_thread_function (void *closure)
+{
+  while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
+    if (fflush (NULL) != 0)
+      {
+        printf ("error: fflush (NULL): %m\n");
+        abort ();
+      }
+  return NULL;
+}
+
+static void
+create_threads (pthread_t *threads, size_t count, void *(*func) (void *))
+{
+  for (size_t i = 0; i < count; ++i)
+    {
+      int ret = pthread_create (threads + i, NULL, func, NULL);
+      if (ret != 0)
+        {
+          errno = ret;
+          printf ("error: pthread_create: %m\n");
+          abort ();
+        }
+    }
+}
+
+static void
+join_threads (pthread_t *threads, size_t count)
+{
+  for (size_t i = 0; i < count; ++i)
+    {
+      int ret = pthread_join (threads[i], NULL);
+      if (ret != 0)
+        {
+          errno = ret;
+          printf ("error: pthread_join: %m\n");
+          abort ();
+        }
+    }
+}
+
+/* Create a file which consists of a single long line, and assigns
+   file_to_read.  The hope is that this triggers an allocation in
+   getline which needs a lock.  */
+static void
+create_file_with_large_line (void)
+{
+  int fd = create_temp_file ("bug19431-large-line", &file_to_read);
+  if (fd < 0)
+    {
+      printf ("error: create_temp_file: %m\n");
+      abort ();
+    }
+  FILE *f = fdopen (fd, "w+");
+  if (f == NULL)
+    {
+      printf ("error: fdopen: %m\n");
+      abort ();
+    }
+  for (int i = 0; i < 50000; ++i)
+    fputc ('x', f);
+  fputc ('\n', f);
+  if (ferror (f))
+    {
+      printf ("error: fputc: %m\n");
+      abort ();
+    }
+  if (fclose (f) != 0)
+    {
+      printf ("error: fclose: %m\n");
+      abort ();
+    }
+}
+
+static int
+do_test (void)
+{
+  /* Make sure that we do not exceed the arena limit with the number
+     of threads we configured.  */
+  if (mallopt (M_ARENA_MAX, 400) == 0)
+    {
+      printf ("error: mallopt (M_ARENA_MAX) failed\n");
+      return 1;
+    }
+
+  /* Leave some room for shutting down all threads gracefully.  */
+  int timeout = 3;
+  if (timeout > TIMEOUT)
+    timeout = TIMEOUT - 1;
+
+  create_file_with_large_line ();
+
+  pthread_t fork_threads[fork_thread_count];
+  create_threads (fork_threads, fork_thread_count, fork_thread_function);
+  pthread_t read_threads[read_thread_count];
+  create_threads (read_threads, read_thread_count, read_thread_function);
+  pthread_t flushall_threads[1];
+  create_threads (flushall_threads, 1, flushall_thread_function);
+
+  struct timespec ts = {timeout, 0};
+  if (nanosleep (&ts, NULL))
+    {
+      printf ("error: error: nanosleep: %m\n");
+      abort ();
+    }
+
+  __atomic_store_n (&termination_requested, true, __ATOMIC_RELAXED);
+
+  join_threads (flushall_threads, 1);
+  join_threads (read_threads, read_thread_count);
+  join_threads (fork_threads, fork_thread_count);
+
+  free (file_to_read);
+
+  return 0;
+}
diff --git a/manual/memory.texi b/manual/memory.texi
index cea2cd7..e94bd4e 100644
--- a/manual/memory.texi
+++ b/manual/memory.texi
@@ -1051,14 +1051,6 @@ systems that do not support @w{ISO C11}.
 @c     _dl_addr_inside_object ok
 @c    determine_info ok
 @c    __rtld_lock_unlock_recursive (dl_load_lock) @aculock
-@c   thread_atfork @asulock @aculock @acsfd @acsmem
-@c    __register_atfork @asulock @aculock @acsfd @acsmem
-@c     lll_lock (__fork_lock) @asulock @aculock
-@c     fork_handler_alloc @asulock @aculock @acsfd @acsmem
-@c      calloc dup @asulock @aculock @acsfd @acsmem
-@c     __linkin_atfork ok
-@c      catomic_compare_and_exchange_bool_acq ok
-@c     lll_unlock (__fork_lock) @aculock
 @c   *_environ @mtsenv
 @c   next_env_entry ok
 @c   strcspn dup ok
diff --git a/sysdeps/mach/hurd/fork.c b/sysdeps/mach/hurd/fork.c
index f3c2615..734c593 100644
--- a/sysdeps/mach/hurd/fork.c
+++ b/sysdeps/mach/hurd/fork.c
@@ -26,6 +26,7 @@
 #include <assert.h>
 #include "hurdmalloc.h"		/* XXX */
 #include <tls.h>
+#include <malloc/malloc-internal.h>
 
 #undef __fork
 
@@ -107,6 +108,12 @@ __fork (void)
       /* Run things that prepare for forking before we create the task.  */
       RUN_HOOK (_hurd_fork_prepare_hook, ());
 
+      /* Acquire malloc locks.  This needs to come last because fork
+	 handlers may use malloc, and the libio list lock has an
+	 indirect malloc dependency as well (via the getdelim
+	 function).  */
+      __malloc_fork_lock_parent ();
+
       /* Lock things that want to be locked before we fork.  */
       {
 	void *const *p;
@@ -608,6 +615,9 @@ __fork (void)
 			   nthreads * sizeof (*threads));
 	}
 
+      /* Release malloc locks.  */
+      __malloc_fork_unlock_parent ();
+
       /* Run things that want to run in the parent to restore it to
 	 normality.  Usually prepare hooks and parent hooks are
 	 symmetrical: the prepare hook arrests state in some way for the
@@ -659,6 +669,9 @@ __fork (void)
       /* Forking clears the trace flag.  */
       __sigemptyset (&_hurdsig_traced);
 
+      /* Release malloc locks.  */
+      __malloc_fork_unlock_child ();
+
       /* Run things that want to run in the child task to set up.  */
       RUN_HOOK (_hurd_fork_child_hook, ());
 
diff --git a/sysdeps/nptl/fork.c b/sysdeps/nptl/fork.c
index 74482b7..87aa91e 100644
--- a/sysdeps/nptl/fork.c
+++ b/sysdeps/nptl/fork.c
@@ -30,7 +30,7 @@
 #include <nptl/pthreadP.h>
 #include <fork.h>
 #include <arch-fork.h>
-
+#include <malloc/malloc-internal.h>
 
 static void
 fresetlockfiles (void)
@@ -110,6 +110,11 @@ __libc_fork (void)
 
   _IO_list_lock ();
 
+  /* Acquire malloc locks.  This needs to come last because fork
+     handlers may use malloc, and the libio list lock has an indirect
+     malloc dependency as well (via the getdelim function).  */
+  __malloc_fork_lock_parent ();
+
 #ifndef NDEBUG
   pid_t ppid = THREAD_GETMEM (THREAD_SELF, tid);
 #endif
@@ -167,6 +172,9 @@ __libc_fork (void)
 # endif
 #endif
 
+      /* Release malloc locks.  */
+      __malloc_fork_unlock_child ();
+
       /* Reset the file list.  These are recursive mutexes.  */
       fresetlockfiles ();
 
@@ -208,6 +216,9 @@ __libc_fork (void)
       /* Restore the PID value.  */
       THREAD_SETMEM (THREAD_SELF, pid, parentpid);
 
+      /* Release malloc locks, parent process variant.  */
+      __malloc_fork_unlock_parent ();
+
       /* We execute this even if the 'fork' call failed.  */
       _IO_list_unlock ();
 

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=b533edfeedf66e971b1b29affd8e2f1619987dc9

commit b533edfeedf66e971b1b29affd8e2f1619987dc9
Author: Florian Weimer <fweimer@redhat.com>
Date:   Mon Dec 21 16:42:46 2015 +0100

    malloc: Fix list_lock/arena lock deadlock [BZ #19182]
    
    	* malloc/arena.c (list_lock): Document lock ordering requirements.
    	(free_list_lock): New lock.
    	(ptmalloc_lock_all): Comment on free_list_lock.
    	(ptmalloc_unlock_all2): Reinitialize free_list_lock.
    	(detach_arena): Update comment.  free_list_lock is now needed.
    	(_int_new_arena): Use free_list_lock around detach_arena call.
    	Acquire arena lock after list_lock.  Add comment, including FIXME
    	about incorrect synchronization.
    	(get_free_list): Switch to free_list_lock.
    	(reused_arena): Acquire free_list_lock around detach_arena call
    	and attached threads counter update.  Add two FIXMEs about
    	incorrect synchronization.
    	(arena_thread_freeres): Switch to free_list_lock.
    	* malloc/malloc.c (struct malloc_state): Update comments to
    	mention free_list_lock.

diff --git a/ChangeLog b/ChangeLog
index 311d1f4..4601726 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+2015-12-21  Florian Weimer  <fweimer@redhat.com>
+
+	[BZ #19182]
+	* malloc/arena.c (list_lock): Document lock ordering requirements.
+	(free_list_lock): New lock.
+	(ptmalloc_lock_all): Comment on free_list_lock.
+	(ptmalloc_unlock_all2): Reinitialize free_list_lock.
+	(detach_arena): Update comment.  free_list_lock is now needed.
+	(_int_new_arena): Use free_list_lock around detach_arena call.
+	Acquire arena lock after list_lock.  Add comment, including FIXME
+	about incorrect synchronization.
+	(get_free_list): Switch to free_list_lock.
+	(reused_arena): Acquire free_list_lock around detach_arena call
+	and attached threads counter update.  Add two FIXMEs about
+	incorrect synchronization.
+	(arena_thread_freeres): Switch to free_list_lock.
+	* malloc/malloc.c (struct malloc_state): Update comments to
+	mention free_list_lock.
+
 2015-11-24  Florian Weimer  <fweimer@redhat.com>
 
 	Replace MUTEX_INITIALIZER with _LIBC_LOCK_INITIALIZER in generic code.
diff --git a/malloc/arena.c b/malloc/arena.c
index f8cf182..6ceb9df 100644
--- a/malloc/arena.c
+++ b/malloc/arena.c
@@ -68,15 +68,28 @@ extern int sanity_check_heap_info_alignment[(sizeof (heap_info)
 
 static __thread mstate thread_arena attribute_tls_model_ie;
 
-/* Arena free list.  list_lock protects the free_list variable below,
-   and the next_free and attached_threads members of the mstate
-   objects.  No other (malloc) locks must be taken while list_lock is
-   active, otherwise deadlocks may occur.  */
+/* Arena free list.  free_list_lock synchronizes access to the
+   free_list variable below, and the next_free and attached_threads
+   members of struct malloc_state objects.  No other locks must be
+   acquired after free_list_lock has been acquired.  */
 
-static mutex_t list_lock = _LIBC_LOCK_INITIALIZER;
+static mutex_t free_list_lock = _LIBC_LOCK_INITIALIZER;
 static size_t narenas = 1;
 static mstate free_list;
 
+/* list_lock prevents concurrent writes to the next member of struct
+   malloc_state objects.
+
+   Read access to the next member is supposed to synchronize with the
+   atomic_write_barrier and the write to the next member in
+   _int_new_arena.  This suffers from data races; see the FIXME
+   comments in _int_new_arena and reused_arena.
+
+   list_lock also prevents concurrent forks.  When list_lock is
+   acquired, no arena lock must be acquired, but it is permitted to
+   acquire arena locks after list_lock.  */
+static mutex_t list_lock = _LIBC_LOCK_INITIALIZER;
+
 /* Mapped memory in non-main arenas (reliable only for NO_THREADS). */
 static unsigned long arena_mem;
 
@@ -207,6 +220,9 @@ ptmalloc_lock_all (void)
   if (__malloc_initialized < 1)
     return;
 
+  /* We do not acquire free_list_lock here because we completely
+     reconstruct free_list in ptmalloc_unlock_all2.  */
+
   if (mutex_trylock (&list_lock))
     {
       if (thread_arena == ATFORK_ARENA_PTR)
@@ -286,6 +302,7 @@ ptmalloc_unlock_all2 (void)
 
   /* Push all arenas to the free list, except save_arena, which is
      attached to the current thread.  */
+  mutex_init (&free_list_lock);
   if (save_arena != NULL)
     ((mstate) save_arena)->attached_threads = 1;
   free_list = NULL;
@@ -303,6 +320,7 @@ ptmalloc_unlock_all2 (void)
       if (ar_ptr == &main_arena)
         break;
     }
+
   mutex_init (&list_lock);
   atfork_recursive_cntr = 0;
 }
@@ -722,7 +740,7 @@ heap_trim (heap_info *heap, size_t pad)
 /* Create a new arena with initial size "size".  */
 
 /* If REPLACED_ARENA is not NULL, detach it from this thread.  Must be
-   called while list_lock is held.  */
+   called while free_list_lock is held.  */
 static void
 detach_arena (mstate replaced_arena)
 {
@@ -775,19 +793,34 @@ _int_new_arena (size_t size)
   mstate replaced_arena = thread_arena;
   thread_arena = a;
   mutex_init (&a->mutex);
-  (void) mutex_lock (&a->mutex);
 
   (void) mutex_lock (&list_lock);
 
-  detach_arena (replaced_arena);
-
   /* Add the new arena to the global list.  */
   a->next = main_arena.next;
+  /* FIXME: The barrier is an attempt to synchronize with read access
+     in reused_arena, which does not acquire list_lock while
+     traversing the list.  */
   atomic_write_barrier ();
   main_arena.next = a;
 
   (void) mutex_unlock (&list_lock);
 
+  (void) mutex_lock (&free_list_lock);
+  detach_arena (replaced_arena);
+  (void) mutex_unlock (&free_list_lock);
+
+  /* Lock this arena.  NB: Another thread may have been attached to
+     this arena because the arena is now accessible from the
+     main_arena.next list and could have been picked by reused_arena.
+     This can only happen for the last arena created (before the arena
+     limit is reached).  At this point, some arena has to be attached
+     to two threads.  We could acquire the arena lock before list_lock
+     to make it less likely that reused_arena picks this new arena,
+     but this could result in a deadlock with ptmalloc_lock_all.  */
+
+  (void) mutex_lock (&a->mutex);
+
   return a;
 }
 
@@ -799,7 +832,7 @@ get_free_list (void)
   mstate result = free_list;
   if (result != NULL)
     {
-      (void) mutex_lock (&list_lock);
+      (void) mutex_lock (&free_list_lock);
       result = free_list;
       if (result != NULL)
 	{
@@ -812,7 +845,7 @@ get_free_list (void)
 
 	  detach_arena (replaced_arena);
 	}
-      (void) mutex_unlock (&list_lock);
+      (void) mutex_unlock (&free_list_lock);
 
       if (result != NULL)
         {
@@ -832,6 +865,7 @@ static mstate
 reused_arena (mstate avoid_arena)
 {
   mstate result;
+  /* FIXME: Access to next_to_use suffers from data races.  */
   static mstate next_to_use;
   if (next_to_use == NULL)
     next_to_use = &main_arena;
@@ -842,6 +876,7 @@ reused_arena (mstate avoid_arena)
       if (!mutex_trylock (&result->mutex))
         goto out;
 
+      /* FIXME: This is a data race, see _int_new_arena.  */
       result = result->next;
     }
   while (result != next_to_use);
@@ -857,11 +892,12 @@ reused_arena (mstate avoid_arena)
 
 out:
   {
+    /* Update the arena thread attachment counters.   */
     mstate replaced_arena = thread_arena;
-    (void) mutex_lock (&list_lock);
+    (void) mutex_lock (&free_list_lock);
     detach_arena (replaced_arena);
     ++result->attached_threads;
-    (void) mutex_unlock (&list_lock);
+    (void) mutex_unlock (&free_list_lock);
   }
 
   LIBC_PROBE (memory_arena_reuse, 2, result, avoid_arena);
@@ -955,7 +991,7 @@ arena_thread_freeres (void)
 
   if (a != NULL)
     {
-      (void) mutex_lock (&list_lock);
+      (void) mutex_lock (&free_list_lock);
       /* If this was the last attached thread for this arena, put the
 	 arena on the free list.  */
       assert (a->attached_threads > 0);
@@ -964,7 +1000,7 @@ arena_thread_freeres (void)
 	  a->next_free = free_list;
 	  free_list = a;
 	}
-      (void) mutex_unlock (&list_lock);
+      (void) mutex_unlock (&free_list_lock);
     }
 }
 text_set_element (__libc_thread_subfreeres, arena_thread_freeres);
diff --git a/malloc/malloc.c b/malloc/malloc.c
index eba20cd..f190309 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -1697,12 +1697,12 @@ struct malloc_state
   struct malloc_state *next;
 
   /* Linked list for free arenas.  Access to this field is serialized
-     by list_lock in arena.c.  */
+     by free_list_lock in arena.c.  */
   struct malloc_state *next_free;
 
   /* Number of threads attached to this arena.  0 if the arena is on
-     the free list.  Access to this field is serialized by list_lock
-     in arena.c.  */
+     the free list.  Access to this field is serialized by
+     free_list_lock in arena.c.  */
   INTERNAL_SIZE_T attached_threads;
 
   /* Memory allocated from the system in this arena.  */

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=4b31bd831ab205ff24ae3a2b8c4f8135c182c7d7

commit 4b31bd831ab205ff24ae3a2b8c4f8135c182c7d7
Author: Florian Weimer <fweimer@redhat.com>
Date:   Tue Nov 24 16:37:15 2015 +0100

    Replace MUTEX_INITIALIZER with _LIBC_LOCK_INITIALIZER in generic code
    
    	* sysdeps/mach/hurd/libc-lock.h (_LIBC_LOCK_INITIALIZER): Define.
    	(__libc_lock_define_initialized): Use it.
    	* sysdeps/nptl/libc-lockP.h (_LIBC_LOCK_INITIALIZER): Define.
    	* malloc/arena.c (list_lock): Use _LIBC_LOCK_INITIALIZER.
    	* malloc/malloc.c (main_arena): Likewise.
    	* sysdeps/generic/malloc-machine.h (MUTEX_INITIALIZER): Remove.
    	* sysdeps/nptl/malloc-machine.h (MUTEX_INITIALIZER): Remove.

diff --git a/ChangeLog b/ChangeLog
index 75615ab..311d1f4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2015-11-24  Florian Weimer  <fweimer@redhat.com>
+
+	Replace MUTEX_INITIALIZER with _LIBC_LOCK_INITIALIZER in generic code.
+	* sysdeps/mach/hurd/libc-lock.h (_LIBC_LOCK_INITIALIZER): Define.
+	(__libc_lock_define_initialized): Use it.
+	* sysdeps/nptl/libc-lockP.h (_LIBC_LOCK_INITIALIZER): Define.
+	* malloc/arena.c (list_lock): Use _LIBC_LOCK_INITIALIZER.
+	* malloc/malloc.c (main_arena): Likewise.
+	* sysdeps/generic/malloc-machine.h (MUTEX_INITIALIZER): Remove.
+	* sysdeps/nptl/malloc-machine.h (MUTEX_INITIALIZER): Remove.
+
 2015-10-28  Florian Weimer  <fweimer@redhat.com>
 
 	[BZ# 19048]
diff --git a/malloc/arena.c b/malloc/arena.c
index dff9870..f8cf182 100644
--- a/malloc/arena.c
+++ b/malloc/arena.c
@@ -73,7 +73,7 @@ static __thread mstate thread_arena attribute_tls_model_ie;
    objects.  No other (malloc) locks must be taken while list_lock is
    active, otherwise deadlocks may occur.  */
 
-static mutex_t list_lock = MUTEX_INITIALIZER;
+static mutex_t list_lock = _LIBC_LOCK_INITIALIZER;
 static size_t narenas = 1;
 static mstate free_list;
 
diff --git a/malloc/malloc.c b/malloc/malloc.c
index 94066d2..eba20cd 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -1747,7 +1747,7 @@ struct malloc_par
 
 static struct malloc_state main_arena =
 {
-  .mutex = MUTEX_INITIALIZER,
+  .mutex = _LIBC_LOCK_INITIALIZER,
   .next = &main_arena,
   .attached_threads = 1
 };
diff --git a/sysdeps/generic/malloc-machine.h b/sysdeps/generic/malloc-machine.h
index 802d1f5..7b7eae8 100644
--- a/sysdeps/generic/malloc-machine.h
+++ b/sysdeps/generic/malloc-machine.h
@@ -38,7 +38,6 @@ typedef int mutex_t;
 # define mutex_lock(m)          ({ *(m) = 1; 0; })
 # define mutex_trylock(m)       (*(m) ? 1 : ((*(m) = 1), 0))
 # define mutex_unlock(m)        (*(m) = 0)
-# define MUTEX_INITIALIZER      (0)
 
 #endif /* !defined mutex_init */
 
diff --git a/sysdeps/mach/hurd/bits/libc-lock.h b/sysdeps/mach/hurd/bits/libc-lock.h
index 24c3aa8..ca9c3e3 100644
--- a/sysdeps/mach/hurd/bits/libc-lock.h
+++ b/sysdeps/mach/hurd/bits/libc-lock.h
@@ -50,8 +50,9 @@ typedef struct __libc_lock_recursive_opaque__ __libc_lock_recursive_t;
   CLASS __libc_lock_t NAME;
 
 /* Define an initialized lock variable NAME with storage class CLASS.  */
+#define _LIBC_LOCK_INITIALIZER MUTEX_INITIALIZER
 #define __libc_lock_define_initialized(CLASS,NAME) \
-  CLASS __libc_lock_t NAME = MUTEX_INITIALIZER;
+  CLASS __libc_lock_t NAME = _LIBC_LOCK_INITIALIZER;
 
 /* Initialize the named lock variable, leaving it in a consistent, unlocked
    state.  */
diff --git a/sysdeps/nptl/bits/libc-lockP.h b/sysdeps/nptl/bits/libc-lockP.h
index f55f621..fae8a2b 100644
--- a/sysdeps/nptl/bits/libc-lockP.h
+++ b/sysdeps/nptl/bits/libc-lockP.h
@@ -75,6 +75,7 @@ typedef pthread_key_t __libc_key_t;
    initialized locks must be set to one due to the lack of normal
    atomic operations.) */
 
+#define _LIBC_LOCK_INITIALIZER LLL_LOCK_INITIALIZER
 #if IS_IN (libc) || IS_IN (libpthread)
 # if LLL_LOCK_INITIALIZER == 0
 #  define __libc_lock_define_initialized(CLASS,NAME) \
diff --git a/sysdeps/nptl/malloc-machine.h b/sysdeps/nptl/malloc-machine.h
index 4d44089..0b679b7 100644
--- a/sysdeps/nptl/malloc-machine.h
+++ b/sysdeps/nptl/malloc-machine.h
@@ -31,7 +31,6 @@ __libc_lock_define (typedef, mutex_t)
 #define mutex_lock(m)		__libc_lock_lock (*(m))
 #define mutex_trylock(m)	__libc_lock_trylock (*(m))
 #define mutex_unlock(m)		__libc_lock_unlock (*(m))
-#define MUTEX_INITIALIZER	LLL_LOCK_INITIALIZER
 
 /* This is defined by newer gcc version unique for each module.  */
 extern void *__dso_handle __attribute__ ((__weak__));

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=50992336ac40625c363e1514890f92774c65054a

commit 50992336ac40625c363e1514890f92774c65054a
Author: Florian Weimer <fweimer@redhat.com>
Date:   Wed Oct 28 19:32:46 2015 +0100

    malloc: Prevent arena free_list from turning cyclic [BZ #19048]
    
    	[BZ# 19048]
    	* malloc/malloc.c (struct malloc_state): Update comment.  Add
    	attached_threads member.
    	(main_arena): Initialize attached_threads.
    	* malloc/arena.c (list_lock): Update comment.
    	(ptmalloc_lock_all, ptmalloc_unlock_all): Likewise.
    	(ptmalloc_unlock_all2): Reinitialize arena reference counts.
    	(deattach_arena): New function.
    	(_int_new_arena): Initialize arena reference count and deattach
    	replaced arena.
    	(get_free_list, reused_arena): Update reference count and deattach
    	replaced arena.
    	(arena_thread_freeres): Update arena reference count and only put
    	unreferenced arenas on the free list.

diff --git a/ChangeLog b/ChangeLog
index 1a7d1fc..75615ab 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2015-10-28  Florian Weimer  <fweimer@redhat.com>
+
+	[BZ# 19048]
+	* malloc/malloc.c (struct malloc_state): Update comment.  Add
+	attached_threads member.
+	(main_arena): Initialize attached_threads.
+	* malloc/arena.c (list_lock): Update comment.
+	(ptmalloc_lock_all, ptmalloc_unlock_all): Likewise.
+	(ptmalloc_unlock_all2): Reinitialize arena reference counts.
+	(deattach_arena): New function.
+	(_int_new_arena): Initialize arena reference count and deattach
+	replaced arena.
+	(get_free_list, reused_arena): Update reference count and deattach
+	replaced arena.
+	(arena_thread_freeres): Update arena reference count and only put
+	unreferenced arenas on the free list.
+
 2015-10-17  Florian Weimer  <fweimer@redhat.com>
 
 	malloc: Rewrite with explicit TLS access using __thread.
diff --git a/NEWS b/NEWS
index c27c29f..147d5ff 100644
--- a/NEWS
+++ b/NEWS
@@ -10,7 +10,7 @@ Version 2.21.1
 * The following bugs are resolved with this release:
 
   17269, 17905, 17949, 18007, 18032, 18080, 18240, 18287, 18508, 18694,
-  18887, 18985, 19682.
+  18887, 18985, 19048, 19682.
 
 * A stack-based buffer overflow was found in libresolv when invoked from
   libnss_dns, allowing specially crafted DNS responses to seize control
diff --git a/malloc/arena.c b/malloc/arena.c
index c726ab6..dff9870 100644
--- a/malloc/arena.c
+++ b/malloc/arena.c
@@ -68,7 +68,10 @@ extern int sanity_check_heap_info_alignment[(sizeof (heap_info)
 
 static __thread mstate thread_arena attribute_tls_model_ie;
 
-/* Arena free list.  */
+/* Arena free list.  list_lock protects the free_list variable below,
+   and the next_free and attached_threads members of the mstate
+   objects.  No other (malloc) locks must be taken while list_lock is
+   active, otherwise deadlocks may occur.  */
 
 static mutex_t list_lock = MUTEX_INITIALIZER;
 static size_t narenas = 1;
@@ -225,7 +228,10 @@ ptmalloc_lock_all (void)
   save_free_hook = __free_hook;
   __malloc_hook = malloc_atfork;
   __free_hook = free_atfork;
-  /* Only the current thread may perform malloc/free calls now. */
+  /* Only the current thread may perform malloc/free calls now.
+     save_arena will be reattached to the current thread, in
+     ptmalloc_lock_all, so save_arena->attached_threads is not
+     updated.  */
   save_arena = thread_arena;
   thread_arena = ATFORK_ARENA_PTR;
 out:
@@ -243,6 +249,9 @@ ptmalloc_unlock_all (void)
   if (--atfork_recursive_cntr != 0)
     return;
 
+  /* Replace ATFORK_ARENA_PTR with save_arena.
+     save_arena->attached_threads was not changed in ptmalloc_lock_all
+     and is still correct.  */
   thread_arena = save_arena;
   __malloc_hook = save_malloc_hook;
   __free_hook = save_free_hook;
@@ -274,12 +283,19 @@ ptmalloc_unlock_all2 (void)
   thread_arena = save_arena;
   __malloc_hook = save_malloc_hook;
   __free_hook = save_free_hook;
+
+  /* Push all arenas to the free list, except save_arena, which is
+     attached to the current thread.  */
+  if (save_arena != NULL)
+    ((mstate) save_arena)->attached_threads = 1;
   free_list = NULL;
   for (ar_ptr = &main_arena;; )
     {
       mutex_init (&ar_ptr->mutex);
       if (ar_ptr != save_arena)
         {
+	  /* This arena is no longer attached to any thread.  */
+	  ar_ptr->attached_threads = 0;
           ar_ptr->next_free = free_list;
           free_list = ar_ptr;
         }
@@ -705,6 +721,22 @@ heap_trim (heap_info *heap, size_t pad)
 
 /* Create a new arena with initial size "size".  */
 
+/* If REPLACED_ARENA is not NULL, detach it from this thread.  Must be
+   called while list_lock is held.  */
+static void
+detach_arena (mstate replaced_arena)
+{
+  if (replaced_arena != NULL)
+    {
+      assert (replaced_arena->attached_threads > 0);
+      /* The current implementation only detaches from main_arena in
+	 case of allocation failure.  This means that it is likely not
+	 beneficial to put the arena on free_list even if the
+	 reference count reaches zero.  */
+      --replaced_arena->attached_threads;
+    }
+}
+
 static mstate
 _int_new_arena (size_t size)
 {
@@ -726,6 +758,7 @@ _int_new_arena (size_t size)
     }
   a = h->ar_ptr = (mstate) (h + 1);
   malloc_init_state (a);
+  a->attached_threads = 1;
   /*a->next = NULL;*/
   a->system_mem = a->max_system_mem = h->size;
   arena_mem += h->size;
@@ -739,12 +772,15 @@ _int_new_arena (size_t size)
   set_head (top (a), (((char *) h + h->size) - ptr) | PREV_INUSE);
 
   LIBC_PROBE (memory_arena_new, 2, a, size);
+  mstate replaced_arena = thread_arena;
   thread_arena = a;
   mutex_init (&a->mutex);
   (void) mutex_lock (&a->mutex);
 
   (void) mutex_lock (&list_lock);
 
+  detach_arena (replaced_arena);
+
   /* Add the new arena to the global list.  */
   a->next = main_arena.next;
   atomic_write_barrier ();
@@ -759,13 +795,23 @@ _int_new_arena (size_t size)
 static mstate
 get_free_list (void)
 {
+  mstate replaced_arena = thread_arena;
   mstate result = free_list;
   if (result != NULL)
     {
       (void) mutex_lock (&list_lock);
       result = free_list;
       if (result != NULL)
-        free_list = result->next_free;
+	{
+	  free_list = result->next_free;
+
+	  /* Arenas on the free list are not attached to any thread.  */
+	  assert (result->attached_threads == 0);
+	  /* But the arena will now be attached to this thread.  */
+	  result->attached_threads = 1;
+
+	  detach_arena (replaced_arena);
+	}
       (void) mutex_unlock (&list_lock);
 
       if (result != NULL)
@@ -810,6 +856,14 @@ reused_arena (mstate avoid_arena)
   (void) mutex_lock (&result->mutex);
 
 out:
+  {
+    mstate replaced_arena = thread_arena;
+    (void) mutex_lock (&list_lock);
+    detach_arena (replaced_arena);
+    ++result->attached_threads;
+    (void) mutex_unlock (&list_lock);
+  }
+
   LIBC_PROBE (memory_arena_reuse, 2, result, avoid_arena);
   thread_arena = result;
   next_to_use = result->next;
@@ -902,8 +956,14 @@ arena_thread_freeres (void)
   if (a != NULL)
     {
       (void) mutex_lock (&list_lock);
-      a->next_free = free_list;
-      free_list = a;
+      /* If this was the last attached thread for this arena, put the
+	 arena on the free list.  */
+      assert (a->attached_threads > 0);
+      if (--a->attached_threads == 0)
+	{
+	  a->next_free = free_list;
+	  free_list = a;
+	}
       (void) mutex_unlock (&list_lock);
     }
 }
diff --git a/malloc/malloc.c b/malloc/malloc.c
index d2d8038..94066d2 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -1696,9 +1696,15 @@ struct malloc_state
   /* Linked list */
   struct malloc_state *next;
 
-  /* Linked list for free arenas.  */
+  /* Linked list for free arenas.  Access to this field is serialized
+     by list_lock in arena.c.  */
   struct malloc_state *next_free;
 
+  /* Number of threads attached to this arena.  0 if the arena is on
+     the free list.  Access to this field is serialized by list_lock
+     in arena.c.  */
+  INTERNAL_SIZE_T attached_threads;
+
   /* Memory allocated from the system in this arena.  */
   INTERNAL_SIZE_T system_mem;
   INTERNAL_SIZE_T max_system_mem;
@@ -1742,7 +1748,8 @@ struct malloc_par
 static struct malloc_state main_arena =
 {
   .mutex = MUTEX_INITIALIZER,
-  .next = &main_arena
+  .next = &main_arena,
+  .attached_threads = 1
 };
 
 /* There is only one instance of the malloc parameters.  */

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=39ad1d12448adf0682ffb8b8ac146e99b68279bf

commit 39ad1d12448adf0682ffb8b8ac146e99b68279bf
Author: Florian Weimer <fweimer@redhat.com>
Date:   Sat Oct 17 12:06:48 2015 +0200

    malloc: Rewrite with explicit TLS access using __thread

diff --git a/ChangeLog b/ChangeLog
index 12f2687..1a7d1fc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2015-10-17  Florian Weimer  <fweimer@redhat.com>
+
+	malloc: Rewrite with explicit TLS access using __thread.
+	* sysdeps/generic/malloc-machine.h (tsd_key_t, tsd_key_create)
+	(tsd_setspecific, tsd_getspecific): Remove.
+	* sysdeps/mach/hurd/malloc-machine.h (tsd_key_t, tsd_key_create)
+	(tsd_setspecific, tsd_getspecific): Likewise.
+	* sysdeps/nptl/malloc-machine.h (tsd_key_t, tsd_key_create)
+	(tsd_setspecific, tsd_getspecific): Likewise.
+	* malloc/arena.c (thread_arena): New TLS variable.
+	(arena_key): Remove variable.
+	(arena_get): Use thread_arena.
+	(arena_lookup): Remove macro.
+	(malloc_atfork, free_atfork, ptmalloc_lock_all)
+	(ptmalloc_unlock_all, ptmalloc_unlock_all2, ptmalloc_init)
+	(_int_new_arena, get_free_list, reused_arena)
+	(arena_thread_freeres): Use thread_arena.
+	* manual/memory.texi (Basic Allocation): Remove arena_lookup,
+	tsd_getspecific, tsd_setspecific from safety annotations.
+	(Allocating Cleared Space): Remove arena_lookup from safety
+	annotations.
+
 2015-02-18  Siddhesh Poyarekar  <siddhesh@redhat.com>
 
 	* malloc/malloc.c (__libc_malloc): Consolidate arena_lookup and
diff --git a/malloc/arena.c b/malloc/arena.c
index 886defb..c726ab6 100644
--- a/malloc/arena.c
+++ b/malloc/arena.c
@@ -64,9 +64,12 @@ extern int sanity_check_heap_info_alignment[(sizeof (heap_info)
                                              + 2 * SIZE_SZ) % MALLOC_ALIGNMENT
                                             ? -1 : 1];
 
-/* Thread specific data */
+/* Thread specific data.  */
+
+static __thread mstate thread_arena attribute_tls_model_ie;
+
+/* Arena free list.  */
 
-static tsd_key_t arena_key;
 static mutex_t list_lock = MUTEX_INITIALIZER;
 static size_t narenas = 1;
 static mstate free_list;
@@ -89,15 +92,10 @@ int __malloc_initialized = -1;
    in the new arena. */
 
 #define arena_get(ptr, size) do { \
-      arena_lookup (ptr);						      \
+      ptr = thread_arena;						      \
       arena_lock (ptr, size);						      \
   } while (0)
 
-#define arena_lookup(ptr) do { \
-      void *vptr = NULL;						      \
-      ptr = (mstate) tsd_getspecific (arena_key, vptr);			      \
-  } while (0)
-
 #define arena_lock(ptr, size) do {					      \
       if (ptr)								      \
         (void) mutex_lock (&ptr->mutex);				      \
@@ -138,11 +136,9 @@ ATFORK_MEM;
 static void *
 malloc_atfork (size_t sz, const void *caller)
 {
-  void *vptr = NULL;
   void *victim;
 
-  tsd_getspecific (arena_key, vptr);
-  if (vptr == ATFORK_ARENA_PTR)
+  if (thread_arena == ATFORK_ARENA_PTR)
     {
       /* We are the only thread that may allocate at all.  */
       if (save_malloc_hook != malloc_check)
@@ -172,7 +168,6 @@ malloc_atfork (size_t sz, const void *caller)
 static void
 free_atfork (void *mem, const void *caller)
 {
-  void *vptr = NULL;
   mstate ar_ptr;
   mchunkptr p;                          /* chunk corresponding to mem */
 
@@ -188,8 +183,7 @@ free_atfork (void *mem, const void *caller)
     }
 
   ar_ptr = arena_for_chunk (p);
-  tsd_getspecific (arena_key, vptr);
-  _int_free (ar_ptr, p, vptr == ATFORK_ARENA_PTR);
+  _int_free (ar_ptr, p, thread_arena == ATFORK_ARENA_PTR);
 }
 
 
@@ -212,9 +206,7 @@ ptmalloc_lock_all (void)
 
   if (mutex_trylock (&list_lock))
     {
-      void *my_arena;
-      tsd_getspecific (arena_key, my_arena);
-      if (my_arena == ATFORK_ARENA_PTR)
+      if (thread_arena == ATFORK_ARENA_PTR)
         /* This is the same thread which already locks the global list.
            Just bump the counter.  */
         goto out;
@@ -234,8 +226,8 @@ ptmalloc_lock_all (void)
   __malloc_hook = malloc_atfork;
   __free_hook = free_atfork;
   /* Only the current thread may perform malloc/free calls now. */
-  tsd_getspecific (arena_key, save_arena);
-  tsd_setspecific (arena_key, ATFORK_ARENA_PTR);
+  save_arena = thread_arena;
+  thread_arena = ATFORK_ARENA_PTR;
 out:
   ++atfork_recursive_cntr;
 }
@@ -251,7 +243,7 @@ ptmalloc_unlock_all (void)
   if (--atfork_recursive_cntr != 0)
     return;
 
-  tsd_setspecific (arena_key, save_arena);
+  thread_arena = save_arena;
   __malloc_hook = save_malloc_hook;
   __free_hook = save_free_hook;
   for (ar_ptr = &main_arena;; )
@@ -279,7 +271,7 @@ ptmalloc_unlock_all2 (void)
   if (__malloc_initialized < 1)
     return;
 
-  tsd_setspecific (arena_key, save_arena);
+  thread_arena = save_arena;
   __malloc_hook = save_malloc_hook;
   __free_hook = save_free_hook;
   free_list = NULL;
@@ -372,8 +364,7 @@ ptmalloc_init (void)
     __morecore = __failing_morecore;
 #endif
 
-  tsd_key_create (&arena_key, NULL);
-  tsd_setspecific (arena_key, (void *) &main_arena);
+  thread_arena = &main_arena;
   thread_atfork (ptmalloc_lock_all, ptmalloc_unlock_all, ptmalloc_unlock_all2);
   const char *s = NULL;
   if (__glibc_likely (_environ != NULL))
@@ -748,7 +739,7 @@ _int_new_arena (size_t size)
   set_head (top (a), (((char *) h + h->size) - ptr) | PREV_INUSE);
 
   LIBC_PROBE (memory_arena_new, 2, a, size);
-  tsd_setspecific (arena_key, (void *) a);
+  thread_arena = a;
   mutex_init (&a->mutex);
   (void) mutex_lock (&a->mutex);
 
@@ -781,7 +772,7 @@ get_free_list (void)
         {
           LIBC_PROBE (memory_arena_reuse_free_list, 1, result);
           (void) mutex_lock (&result->mutex);
-          tsd_setspecific (arena_key, (void *) result);
+	  thread_arena = result;
         }
     }
 
@@ -820,7 +811,7 @@ reused_arena (mstate avoid_arena)
 
 out:
   LIBC_PROBE (memory_arena_reuse, 2, result, avoid_arena);
-  tsd_setspecific (arena_key, (void *) result);
+  thread_arena = result;
   next_to_use = result->next;
 
   return result;
@@ -905,9 +896,8 @@ arena_get_retry (mstate ar_ptr, size_t bytes)
 static void __attribute__ ((section ("__libc_thread_freeres_fn")))
 arena_thread_freeres (void)
 {
-  void *vptr = NULL;
-  mstate a = tsd_getspecific (arena_key, vptr);
-  tsd_setspecific (arena_key, NULL);
+  mstate a = thread_arena;
+  thread_arena = NULL;
 
   if (a != NULL)
     {
diff --git a/manual/memory.texi b/manual/memory.texi
index 0729e70..cea2cd7 100644
--- a/manual/memory.texi
+++ b/manual/memory.texi
@@ -332,8 +332,6 @@ this function is in @file{stdlib.h}.
 @c __libc_malloc @asulock @aculock @acsfd @acsmem
 @c  force_reg ok
 @c  *malloc_hook unguarded
-@c  arena_lookup ok
-@c   tsd_getspecific ok, TLS
 @c  arena_lock @asulock @aculock @acsfd @acsmem
 @c   mutex_lock @asulock @aculock
 @c   arena_get2 @asulock @aculock @acsfd @acsmem
@@ -341,7 +339,6 @@ this function is in @file{stdlib.h}.
 @c     mutex_lock (list_lock) dup @asulock @aculock
 @c     mutex_unlock (list_lock) dup @aculock
 @c     mutex_lock (arena lock) dup @asulock @aculock [returns locked]
-@c     tsd_setspecific ok, TLS
 @c    __get_nprocs ext ok @acsfd
 @c    NARENAS_FROM_NCORES ok
 @c    catomic_compare_and_exchange_bool_acq ok
@@ -835,7 +832,6 @@ is declared in @file{stdlib.h}.
 @c  *__malloc_hook dup unguarded
 @c  memset dup ok
 @c  arena_get @asulock @aculock @acsfd @acsmem
-@c   arena_lookup dup ok
 @c   arena_lock dup @asulock @aculock @acsfd @acsmem
 @c  top dup ok
 @c  chunksize dup ok
diff --git a/sysdeps/generic/malloc-machine.h b/sysdeps/generic/malloc-machine.h
index 10f6e72..802d1f5 100644
--- a/sysdeps/generic/malloc-machine.h
+++ b/sysdeps/generic/malloc-machine.h
@@ -40,13 +40,6 @@ typedef int mutex_t;
 # define mutex_unlock(m)        (*(m) = 0)
 # define MUTEX_INITIALIZER      (0)
 
-typedef void *tsd_key_t;
-# define tsd_key_create(key, destr) do {} while(0)
-# define tsd_setspecific(key, data) ((key) = (data))
-# define tsd_getspecific(key, vptr) (vptr = (key))
-
-# define thread_atfork(prepare, parent, child) do {} while(0)
-
 #endif /* !defined mutex_init */
 
 #ifndef atomic_full_barrier
diff --git a/sysdeps/mach/hurd/malloc-machine.h b/sysdeps/mach/hurd/malloc-machine.h
index f7fb96e..06e6e18 100644
--- a/sysdeps/mach/hurd/malloc-machine.h
+++ b/sysdeps/mach/hurd/malloc-machine.h
@@ -52,16 +52,6 @@
 /* No we're *not* using pthreads.  */
 #define __pthread_initialize ((void (*)(void))0)
 
-/* thread specific data for glibc */
-
-#include <bits/libc-tsd.h>
-
-typedef int tsd_key_t[1];	/* no key data structure, libc magic does it */
-__libc_tsd_define (static, void *, MALLOC)	/* declaration/common definition */
-#define tsd_key_create(key, destr)	((void) (key))
-#define tsd_setspecific(key, data)	__libc_tsd_set (void *, MALLOC, (data))
-#define tsd_getspecific(key, vptr)	((vptr) = __libc_tsd_get (void *, MALLOC))
-
 /* madvise is a stub on Hurd, so don't bother calling it.  */
 
 #include <sys/mman.h>
diff --git a/sysdeps/nptl/malloc-machine.h b/sysdeps/nptl/malloc-machine.h
index 27c9911..4d44089 100644
--- a/sysdeps/nptl/malloc-machine.h
+++ b/sysdeps/nptl/malloc-machine.h
@@ -58,16 +58,6 @@ extern void *__dso_handle __attribute__ ((__weak__));
   __linkin_atfork (&atfork_mem)
 #endif
 
-/* thread specific data for glibc */
-
-#include <bits/libc-tsd.h>
-
-typedef int tsd_key_t[1];	/* no key data structure, libc magic does it */
-__libc_tsd_define (static, void *, MALLOC)	/* declaration/common definition */
-#define tsd_key_create(key, destr)	((void) (key))
-#define tsd_setspecific(key, data)	__libc_tsd_set (void *, MALLOC, (data))
-#define tsd_getspecific(key, vptr)	((vptr) = __libc_tsd_get (void *, MALLOC))
-
 #include <sysdeps/generic/malloc-machine.h>
 
 #endif /* !defined(_MALLOC_MACHINE_H) */

http://sourceware.org/git/gitweb.cgi?p=glibc.git;a=commitdiff;h=7f2efd6c269a2ab2eee80bffa5bcb2538aa89641

commit 7f2efd6c269a2ab2eee80bffa5bcb2538aa89641
Author: Siddhesh Poyarekar <siddhesh@redhat.com>
Date:   Wed Feb 18 11:06:05 2015 +0530

    Consolidate arena_lookup and arena_lock into a single arena_get
    
    This seems to have been left behind as an artifact of some old changes
    and can now be merged.  Verified that the only generated code change
    on x86_64 is that of line numbers in asserts, like so:
    
    @@ -27253,7 +27253,7 @@ Disassembly of section .text:
       416f09:      48 89 42 20             mov    %rax,0x20(%rdx)
       416f0d:      e9 7e f6 ff ff          jmpq   416590 <_int_free+0x230>
       416f12:      b9 3f 9f 4a 00          mov    $0x4a9f3f,%ecx
    -  416f17:      ba d5 0f 00 00          mov    $0xfd5,%edx
    +  416f17:      ba d6 0f 00 00          mov    $0xfd6,%edx
       416f1c:      be a8 9b 4a 00          mov    $0x4a9ba8,%esi
       416f21:      bf 6a 9c 4a 00          mov    $0x4a9c6a,%edi
       416f26:      e8 45 e8 ff ff          callq  415770 <__malloc_assert>

diff --git a/ChangeLog b/ChangeLog
index 4f035c4..12f2687 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2015-02-18  Siddhesh Poyarekar  <siddhesh@redhat.com>
+
+	* malloc/malloc.c (__libc_malloc): Consolidate arena_lookup and
+	arena_lock into a single arena_get.
+
 2015-05-22  Roland McGrath  <roland@hack.frob.com>
 
 	* nptl/nptl-init.c (__pthread_initialize_minimal_internal):
diff --git a/malloc/malloc.c b/malloc/malloc.c
index aa7edbf..d2d8038 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -2883,9 +2883,8 @@ __libc_malloc (size_t bytes)
   if (__builtin_expect (hook != NULL, 0))
     return (*hook)(bytes, RETURN_ADDRESS (0));
 
-  arena_lookup (ar_ptr);
+  arena_get (ar_ptr, bytes);
 
-  arena_lock (ar_ptr, bytes);
   if (!ar_ptr)
     return 0;
 

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog                                  |  298 ++++++++++++++++++++++++++++
 NEWS                                       |    4 +-
 csu/libc-tls.c                             |    2 +-
 elf/dl-open.c                              |   12 +-
 elf/dl-reloc.c                             |    6 -
 elf/dl-tls.c                               |  209 ++++++++++++--------
 elf/tlsdeschtab.h                          |    4 +-
 include/libc-symbols.h                     |   15 ++
 malloc/Makefile                            |   33 +++-
 malloc/arena.c                             |  211 +++++++++++++-------
 malloc/malloc-internal.h                   |   32 +++
 malloc/malloc.c                            |   20 ++-
 malloc/tst-interpose-aux-nothread.c        |   20 ++
 malloc/tst-interpose-aux-thread.c          |   20 ++
 malloc/tst-interpose-aux.c                 |  270 +++++++++++++++++++++++++
 malloc/tst-interpose-aux.h                 |   30 +++
 malloc/tst-interpose-nothread.c            |   20 ++
 malloc/tst-interpose-skeleton.c            |  210 ++++++++++++++++++++
 malloc/tst-interpose-static-nothread.c     |   19 ++
 malloc/tst-interpose-static-thread.c       |   19 ++
 malloc/tst-interpose-thread.c              |   20 ++
 malloc/tst-malloc-fork-deadlock.c          |  220 ++++++++++++++++++++
 manual/memory.texi                         |   12 --
 nptl/Makefile                              |    8 +-
 nptl/allocatestack.c                       |   13 +-
 nptl/tst-tls3-malloc.c                     |   31 +++
 nptl/tst-tls3.c                            |   10 +-
 nptl_db/db-symbols.awk                     |    2 +
 nptl_db/db_info.c                          |    4 +
 nptl_db/fetch-value.c                      |    3 +-
 nptl_db/structs.def                        |   39 ++++
 nptl_db/td_symbol_list.c                   |    7 +-
 nptl_db/td_thr_tlsbase.c                   |  172 ++++++++++++++++-
 nptl_db/thread_dbP.h                       |   11 +-
 scripts/check-localplt.awk                 |   40 ++++-
 scripts/localplt.awk                       |   51 +++++-
 sysdeps/aarch64/dl-tls.h                   |    3 -
 sysdeps/aarch64/nptl/tls.h                 |   12 +-
 sysdeps/alpha/dl-tls.h                     |    3 -
 sysdeps/alpha/nptl/tls.h                   |   12 +-
 sysdeps/arm/dl-tls.h                       |    3 -
 sysdeps/arm/nptl/tls.h                     |   12 +-
 sysdeps/generic/dl-dtv.h                   |   38 ++++
 sysdeps/generic/malloc-machine.h           |    8 -
 sysdeps/hppa/dl-tls.h                      |    3 -
 sysdeps/hppa/nptl/tls.h                    |   12 +-
 sysdeps/i386/dl-tls.h                      |    3 -
 sysdeps/i386/nptl/tls.h                    |   14 +--
 sysdeps/ia64/dl-tls.h                      |    3 -
 sysdeps/ia64/nptl/tls.h                    |   14 +--
 sysdeps/m68k/dl-tls.h                      |    3 -
 sysdeps/m68k/nptl/tls.h                    |   12 +-
 sysdeps/mach/hurd/bits/libc-lock.h         |    3 +-
 sysdeps/mach/hurd/fork.c                   |   13 ++
 sysdeps/mach/hurd/i386/tls.h               |   12 +-
 sysdeps/mach/hurd/malloc-machine.h         |   10 -
 sysdeps/microblaze/dl-tls.h                |    3 -
 sysdeps/microblaze/nptl/tls.h              |   12 +-
 sysdeps/mips/dl-tls.h                      |    3 -
 sysdeps/mips/nptl/tls.h                    |   13 +-
 sysdeps/nios2/dl-tls.h                     |    3 -
 sysdeps/nios2/nptl/tls.h                   |   12 +-
 sysdeps/nptl/bits/libc-lockP.h             |    1 +
 sysdeps/nptl/fork.c                        |   14 ++-
 sysdeps/nptl/malloc-machine.h              |   11 -
 sysdeps/powerpc/dl-tls.h                   |    3 -
 sysdeps/powerpc/nptl/tls.h                 |   12 +-
 sysdeps/s390/dl-tls.h                      |    3 -
 sysdeps/s390/nptl/tls.h                    |   14 +--
 sysdeps/sh/dl-tls.h                        |    3 -
 sysdeps/sh/nptl/tls.h                      |   12 +-
 sysdeps/sparc/dl-tls.h                     |    3 -
 sysdeps/sparc/nptl/tls.h                   |   12 +-
 sysdeps/tile/dl-tls.h                      |    3 -
 sysdeps/tile/nptl/tls.h                    |   12 +-
 sysdeps/unix/sysv/linux/i386/localplt.data |    8 +-
 sysdeps/x86_64/dl-tls.h                    |    3 -
 sysdeps/x86_64/localplt.data               |   19 ++
 sysdeps/x86_64/nptl/tls.h                  |   13 +--
 test-skeleton.c                            |    2 +
 80 files changed, 1979 insertions(+), 505 deletions(-)
 create mode 100644 malloc/malloc-internal.h
 create mode 100644 malloc/tst-interpose-aux-nothread.c
 create mode 100644 malloc/tst-interpose-aux-thread.c
 create mode 100644 malloc/tst-interpose-aux.c
 create mode 100644 malloc/tst-interpose-aux.h
 create mode 100644 malloc/tst-interpose-nothread.c
 create mode 100644 malloc/tst-interpose-skeleton.c
 create mode 100644 malloc/tst-interpose-static-nothread.c
 create mode 100644 malloc/tst-interpose-static-thread.c
 create mode 100644 malloc/tst-interpose-thread.c
 create mode 100644 malloc/tst-malloc-fork-deadlock.c
 create mode 100644 nptl/tst-tls3-malloc.c
 create mode 100644 sysdeps/generic/dl-dtv.h
 create mode 100644 sysdeps/x86_64/localplt.data


hooks/post-receive
-- 
GNU C Library master sources


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]