diff options
-rw-r--r-- | ChangeLog | 34 | ||||
-rw-r--r-- | include/libc-symbols.h | 15 | ||||
-rw-r--r-- | malloc/Makefile | 27 | ||||
-rw-r--r-- | malloc/tst-interpose-aux-nothread.c | 20 | ||||
-rw-r--r-- | malloc/tst-interpose-aux-thread.c | 20 | ||||
-rw-r--r-- | malloc/tst-interpose-aux.c | 270 | ||||
-rw-r--r-- | malloc/tst-interpose-aux.h | 30 | ||||
-rw-r--r-- | malloc/tst-interpose-nothread.c | 20 | ||||
-rw-r--r-- | malloc/tst-interpose-skeleton.c | 204 | ||||
-rw-r--r-- | malloc/tst-interpose-static-nothread.c | 19 | ||||
-rw-r--r-- | malloc/tst-interpose-static-thread.c | 19 | ||||
-rw-r--r-- | malloc/tst-interpose-thread.c | 20 | ||||
-rw-r--r-- | nptl/tst-tls3-malloc.c | 156 | ||||
-rw-r--r-- | sysdeps/mach/hurd/fork.c | 6 | ||||
-rw-r--r-- | sysdeps/nptl/fork.c | 6 |
15 files changed, 706 insertions, 160 deletions
diff --git a/ChangeLog b/ChangeLog index 995b440c12..bf76d97258 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-26 Adhemerval Zanella <adhemerval.zanella@linaro.org> * test-skeleton.c (delayed_exit_thread): Add initializer on struct diff --git a/include/libc-symbols.h b/include/libc-symbols.h index c2b499ae36..e362d42095 100644 --- a/include/libc-symbols.h +++ b/include/libc-symbols.h @@ -121,6 +121,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 4d5c81dce6..037e830f31 100644 --- a/malloc/Makefile +++ b/malloc/Makefile @@ -30,7 +30,16 @@ tests := mallocbug tst-malloc tst-valloc tst-calloc tst-obstack \ tst-pvalloc tst-memalign tst-mallopt tst-scratch_buffer \ tst-malloc-backtrace tst-malloc-thread-exit \ tst-malloc-thread-fail tst-malloc-fork-deadlock \ - tst-mallocfork2 + tst-mallocfork2 \ + 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 \ @@ -44,6 +53,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)) @@ -170,3 +188,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 0000000000..0eae66fa6c --- /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 0000000000..354e4d8ed1 --- /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 0000000000..77866b2e5d --- /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__ (max_align_t)))) 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 0000000000..2fb22d312a --- /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 0000000000..9acb572098 --- /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 0000000000..d1ebc9ae2d --- /dev/null +++ b/malloc/tst-interpose-skeleton.c @@ -0,0 +1,204 @@ +/* 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 = xpthread_create (NULL, run_tests, NULL); + xpthread_join (thr); +#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 0000000000..3fb2dd8777 --- /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 0000000000..c78ebc70ba --- /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 0000000000..d3e20c7457 --- /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 8a580fa789..719ab28cf0 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 2e8b59e7c0..9973b1bb9e 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. */ { @@ -612,7 +612,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 @@ -666,7 +666,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 616d897a36..ea135f8e18 100644 --- a/sysdeps/nptl/fork.c +++ b/sysdeps/nptl/fork.c @@ -128,7 +128,7 @@ __libc_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); } #ifndef NDEBUG @@ -192,7 +192,7 @@ __libc_fork (void) if (multiple_threads) { /* Release malloc locks. */ - __malloc_fork_unlock_child (); + call_function_static_weak (__malloc_fork_unlock_child); /* Reset the file list. These are recursive mutexes. */ fresetlockfiles (); @@ -240,7 +240,7 @@ __libc_fork (void) if (multiple_threads) { /* 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 (); |