summaryrefslogtreecommitdiff
path: root/nptl/tst-tls3-malloc.c
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2016-08-03 16:16:57 +0200
committerFlorian Weimer <fweimer@redhat.com>2016-08-03 16:16:57 +0200
commit6c444ad6e953dbdf9c7be065308a0a7779d32bb2 (patch)
tree3fb6aaaf2ac1d840ec6cebd39944d17f54b78b95 /nptl/tst-tls3-malloc.c
parenta2ff21f825adb8821eeb6145197fa8b9a8a60a58 (diff)
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.
Diffstat (limited to 'nptl/tst-tls3-malloc.c')
-rw-r--r--nptl/tst-tls3-malloc.c176
1 files changed, 176 insertions, 0 deletions
diff --git a/nptl/tst-tls3-malloc.c b/nptl/tst-tls3-malloc.c
new file mode 100644
index 0000000000..5eab3cdbb4
--- /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);
+}