diff options
Diffstat (limited to 'nptl')
-rw-r--r-- | nptl/Makefile | 8 | ||||
-rw-r--r-- | nptl/tst-tls3-malloc.c | 176 |
2 files changed, 182 insertions, 2 deletions
diff --git a/nptl/Makefile b/nptl/Makefile index 0d8aadebed..2ddcd2b4e4 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -321,8 +321,8 @@ tests += tst-cancelx2 tst-cancelx3 tst-cancelx4 tst-cancelx5 \ tst-cleanupx0 tst-cleanupx1 tst-cleanupx2 tst-cleanupx3 tst-cleanupx4 \ tst-oncex3 tst-oncex4 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 @@ -529,6 +529,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 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); +} |