summaryrefslogtreecommitdiff
path: root/malloc
diff options
context:
space:
mode:
Diffstat (limited to 'malloc')
-rw-r--r--malloc/Makefile118
-rw-r--r--malloc/Versions20
-rw-r--r--malloc/alloc_buffer_alloc_array.c47
-rw-r--r--malloc/alloc_buffer_allocate.c36
-rw-r--r--malloc/alloc_buffer_copy_bytes.c34
-rw-r--r--malloc/alloc_buffer_copy_string.c30
-rw-r--r--malloc/alloc_buffer_create_failure.c31
-rw-r--r--malloc/arena.c388
-rw-r--r--malloc/dynarray-skeleton.c521
-rw-r--r--malloc/dynarray.h179
-rw-r--r--malloc/dynarray_at_failure.c31
-rw-r--r--malloc/dynarray_emplace_enlarge.c73
-rw-r--r--malloc/dynarray_finalize.c62
-rw-r--r--malloc/dynarray_resize.c64
-rw-r--r--malloc/dynarray_resize_clear.c35
-rw-r--r--malloc/hooks.c379
-rw-r--r--malloc/malloc-hooks.h24
-rw-r--r--malloc/malloc-internal.h97
-rw-r--r--malloc/malloc.c1580
-rw-r--r--malloc/malloc.h27
-rw-r--r--malloc/mcheck-init.c5
-rw-r--r--malloc/mcheck.c52
-rw-r--r--malloc/mcheck.h2
-rw-r--r--malloc/memusage.c2
-rwxr-xr-xmalloc/memusage.sh4
-rw-r--r--malloc/memusagestat.c4
-rw-r--r--malloc/morecore.c2
-rw-r--r--malloc/mtrace.c75
-rw-r--r--malloc/mtrace.pl4
-rw-r--r--malloc/obstack.c2
-rw-r--r--malloc/obstack.h2
-rw-r--r--malloc/reallocarray.c37
-rw-r--r--malloc/scratch_buffer_grow.c8
-rw-r--r--malloc/scratch_buffer_grow_preserve.c12
-rw-r--r--malloc/scratch_buffer_set_array_size.c9
-rw-r--r--malloc/set-freeres.c17
-rw-r--r--malloc/thread-freeres.c26
-rw-r--r--malloc/tst-alloc_buffer.c665
-rw-r--r--malloc/tst-calloc.c2
-rw-r--r--malloc/tst-dynarray-at-fail.c125
-rw-r--r--malloc/tst-dynarray-fail.c418
-rw-r--r--malloc/tst-dynarray-shared.h80
-rw-r--r--malloc/tst-dynarray.c574
-rw-r--r--malloc/tst-interpose-aux-nothread.c20
-rw-r--r--malloc/tst-interpose-aux-thread.c20
-rw-r--r--malloc/tst-interpose-aux.c271
-rw-r--r--malloc/tst-interpose-aux.h30
-rw-r--r--malloc/tst-interpose-nothread.c20
-rw-r--r--malloc/tst-interpose-skeleton.c204
-rw-r--r--malloc/tst-interpose-static-nothread.c19
-rw-r--r--malloc/tst-interpose-static-thread.c19
-rw-r--r--malloc/tst-interpose-thread.c20
-rw-r--r--malloc/tst-malloc-backtrace.c16
-rw-r--r--malloc/tst-malloc-fork-deadlock.c206
-rw-r--r--malloc/tst-malloc-stats-cancellation.c216
-rw-r--r--malloc/tst-malloc-tcache-leak.c113
-rw-r--r--malloc/tst-malloc-thread-exit.c138
-rw-r--r--malloc/tst-malloc-thread-fail.c4
-rw-r--r--malloc/tst-malloc-too-large.c253
-rw-r--r--malloc/tst-malloc-usable-static-tunables.c1
-rw-r--r--malloc/tst-malloc-usable-static.c1
-rw-r--r--malloc/tst-malloc-usable-tunables.c1
-rw-r--r--malloc/tst-malloc-usable.c2
-rw-r--r--malloc/tst-malloc.c17
-rw-r--r--malloc/tst-malloc_info.c101
-rw-r--r--malloc/tst-mallocfork.c2
-rw-r--r--malloc/tst-mallocfork2.c211
-rw-r--r--malloc/tst-mallocstate.c499
-rw-r--r--malloc/tst-mallopt.c2
-rw-r--r--malloc/tst-mcheck.c24
-rw-r--r--malloc/tst-memalign.c2
-rw-r--r--malloc/tst-mtrace.c2
-rwxr-xr-xmalloc/tst-mtrace.sh2
-rw-r--r--malloc/tst-posix_memalign.c2
-rw-r--r--malloc/tst-pvalloc.c2
-rw-r--r--malloc/tst-realloc.c21
-rw-r--r--malloc/tst-reallocarray.c118
-rw-r--r--malloc/tst-scratch_buffer.c4
-rw-r--r--malloc/tst-valloc.c2
79 files changed, 7060 insertions, 1428 deletions
diff --git a/malloc/Makefile b/malloc/Makefile
index 360288bef8..7d54bad866 100644
--- a/malloc/Makefile
+++ b/malloc/Makefile
@@ -1,4 +1,4 @@
-# Copyright (C) 1991-2016 Free Software Foundation, Inc.
+# Copyright (C) 1991-2018 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
@@ -25,16 +25,54 @@ include ../Makeconfig
dist-headers := malloc.h
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-scratch_buffer \
+ tst-mcheck tst-mallocfork tst-trim1 \
+ tst-malloc-usable tst-realloc tst-reallocarray tst-posix_memalign \
+ tst-pvalloc tst-memalign tst-mallopt \
tst-malloc-backtrace tst-malloc-thread-exit \
- tst-malloc-thread-fail
+ tst-malloc-thread-fail tst-malloc-fork-deadlock \
+ tst-mallocfork2 \
+ tst-interpose-nothread \
+ tst-interpose-thread \
+ tst-alloc_buffer \
+ tst-malloc-tcache-leak \
+ tst-malloc_info \
+ tst-malloc-too-large \
+ tst-malloc-stats-cancellation \
+
+tests-static := \
+ tst-interpose-static-nothread \
+ tst-interpose-static-thread \
+ tst-malloc-usable-static \
+
+tests-internal := tst-mallocstate tst-scratch_buffer
+
+# The dynarray framework is only available inside glibc.
+tests-internal += \
+ tst-dynarray \
+ tst-dynarray-fail \
+ tst-dynarray-at-fail \
+
+ifneq (no,$(have-tunables))
+tests += tst-malloc-usable-tunables
+tests-static += tst-malloc-usable-static-tunables
+endif
+
+tests += $(tests-static)
test-srcs = tst-mtrace
-routines = malloc morecore mcheck mtrace obstack \
+routines = malloc morecore mcheck mtrace obstack reallocarray \
scratch_buffer_grow scratch_buffer_grow_preserve \
- scratch_buffer_set_array_size
+ scratch_buffer_set_array_size \
+ dynarray_at_failure \
+ dynarray_emplace_enlarge \
+ dynarray_finalize \
+ dynarray_resize \
+ dynarray_resize_clear \
+ alloc_buffer_alloc_array \
+ alloc_buffer_allocate \
+ alloc_buffer_copy_bytes \
+ alloc_buffer_copy_string \
+ alloc_buffer_create_failure \
install-lib := libmcheck.a
non-lib.a := libmcheck.a
@@ -43,18 +81,30 @@ 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))
-$(objpfx)tst-malloc-backtrace: $(common-objpfx)nptl/libpthread.so \
- $(common-objpfx)nptl/libpthread_nonshared.a
-$(objpfx)tst-malloc-thread-exit: $(common-objpfx)nptl/libpthread.so \
- $(common-objpfx)nptl/libpthread_nonshared.a
-$(objpfx)tst-malloc-thread-fail: $(common-objpfx)nptl/libpthread.so \
- $(common-objpfx)nptl/libpthread_nonshared.a
+$(objpfx)tst-malloc-backtrace: $(shared-thread-library)
+$(objpfx)tst-malloc-thread-exit: $(shared-thread-library)
+$(objpfx)tst-malloc-thread-fail: $(shared-thread-library)
+$(objpfx)tst-malloc-fork-deadlock: $(shared-thread-library)
+$(objpfx)tst-malloc-stats-cancellation: $(shared-thread-library)
+
+# Export the __malloc_initialize_hook variable to libc.so.
+LDFLAGS-tst-mallocstate = -rdynamic
# These should be removed by `make clean'.
extra-objs = mcheck-init.o libmcheck.a
+others-extras = mcheck-init.o
# Include the cleanup handler.
aux := set-freeres thread-freeres
@@ -101,7 +151,7 @@ memusagestat-modules = memusagestat
cpp-srcs-left := $(memusagestat-modules)
lib := memusagestat
-include $(patsubst %,$(..)cppflags-iterator.mk,$(cpp-srcs-left))
+include $(patsubst %,$(..)libof-iterator.mk,$(cpp-srcs-left))
$(objpfx)memusagestat: $(memusagestat-modules:%=$(objpfx)%.o)
$(LINK.o) -o $@ $^ $(libgd-LDFLAGS) -lgd -lpng -lz -lm
@@ -110,14 +160,16 @@ ifeq ($(run-built-tests),yes)
ifeq (yes,$(build-shared))
ifneq ($(PERL),no)
tests-special += $(objpfx)tst-mtrace.out
+tests-special += $(objpfx)tst-dynarray-mem.out
+tests-special += $(objpfx)tst-dynarray-fail-mem.out
endif
endif
endif
include ../Rules
-CFLAGS-mcheck-init.c = $(PIC-ccflag)
-CFLAGS-obstack.c = $(uses-callbacks)
+CFLAGS-mcheck-init.c += $(PIC-ccflag)
+CFLAGS-obstack.c += $(uses-callbacks)
$(objpfx)libmcheck.a: $(objpfx)mcheck-init.o
-rm -f $@
@@ -131,7 +183,7 @@ ifeq (yes,$(build-shared))
ifneq ($(PERL),no)
$(objpfx)tst-mtrace.out: tst-mtrace.sh $(objpfx)tst-mtrace
$(SHELL) $< $(common-objpfx) '$(test-program-prefix-before-env)' \
- '$(run-program-env)' '$(test-program-prefix-after-env)' ; \
+ '$(run-program-env)' '$(test-program-prefix-after-env)' > $@; \
$(evaluate-test)
endif
endif
@@ -139,7 +191,15 @@ endif
tst-mcheck-ENV = MALLOC_CHECK_=3
tst-malloc-usable-ENV = MALLOC_CHECK_=3
+tst-malloc-usable-static-ENV = $(tst-malloc-usable-ENV)
+tst-malloc-usable-tunables-ENV = GLIBC_TUNABLES=glibc.malloc.check=3
+tst-malloc-usable-static-tunables-ENV = $(tst-malloc-usable-tunables-ENV)
+ifeq ($(experimental-malloc),yes)
+CPPFLAGS-malloc.c += -DUSE_TCACHE=1
+else
+CPPFLAGS-malloc.c += -DUSE_TCACHE=0
+endif
# Uncomment this for test releases. For public releases it is too expensive.
#CPPFLAGS-malloc.o += -DMALLOC_DEBUG=1
@@ -167,3 +227,27 @@ $(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
+
+$(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)
+
+tst-dynarray-ENV = MALLOC_TRACE=$(objpfx)tst-dynarray.mtrace
+$(objpfx)tst-dynarray-mem.out: $(objpfx)tst-dynarray.out
+ $(common-objpfx)malloc/mtrace $(objpfx)tst-dynarray.mtrace > $@; \
+ $(evaluate-test)
+
+tst-dynarray-fail-ENV = MALLOC_TRACE=$(objpfx)tst-dynarray-fail.mtrace
+$(objpfx)tst-dynarray-fail-mem.out: $(objpfx)tst-dynarray-fail.out
+ $(common-objpfx)malloc/mtrace $(objpfx)tst-dynarray-fail.mtrace > $@; \
+ $(evaluate-test)
+
+$(objpfx)tst-malloc-tcache-leak: $(shared-thread-library)
+$(objpfx)tst-malloc_info: $(shared-thread-library)
diff --git a/malloc/Versions b/malloc/Versions
index f3c3d8a093..2357cff3da 100644
--- a/malloc/Versions
+++ b/malloc/Versions
@@ -61,6 +61,9 @@ libc {
GLIBC_2.16 {
aligned_alloc;
}
+ GLIBC_2.26 {
+ reallocarray;
+ }
GLIBC_PRIVATE {
# Internal startup hook for libpthread.
__libc_malloc_pthread_startup;
@@ -72,5 +75,22 @@ libc {
__libc_scratch_buffer_grow;
__libc_scratch_buffer_grow_preserve;
__libc_scratch_buffer_set_array_size;
+
+ # Internal name for reallocarray
+ __libc_reallocarray;
+
+ # dynarray support
+ __libc_dynarray_at_failure;
+ __libc_dynarray_emplace_enlarge;
+ __libc_dynarray_finalize;
+ __libc_dynarray_resize;
+ __libc_dynarray_resize_clear;
+
+ # struct alloc_buffer support
+ __libc_alloc_buffer_alloc_array;
+ __libc_alloc_buffer_allocate;
+ __libc_alloc_buffer_copy_bytes;
+ __libc_alloc_buffer_copy_string;
+ __libc_alloc_buffer_create_failure;
}
}
diff --git a/malloc/alloc_buffer_alloc_array.c b/malloc/alloc_buffer_alloc_array.c
new file mode 100644
index 0000000000..1dd098a8fc
--- /dev/null
+++ b/malloc/alloc_buffer_alloc_array.c
@@ -0,0 +1,47 @@
+/* Array allocation from a fixed-size buffer.
+ Copyright (C) 2017-2018 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/>. */
+
+#include <alloc_buffer.h>
+#include <malloc-internal.h>
+#include <libc-pointer-arith.h>
+
+void *
+__libc_alloc_buffer_alloc_array (struct alloc_buffer *buf, size_t element_size,
+ size_t align, size_t count)
+{
+ size_t current = buf->__alloc_buffer_current;
+ /* The caller asserts that align is a power of two. */
+ size_t aligned = ALIGN_UP (current, align);
+ size_t size;
+ bool overflow = check_mul_overflow_size_t (element_size, count, &size);
+ size_t new_current = aligned + size;
+ if (!overflow /* Multiplication did not overflow. */
+ && aligned >= current /* No overflow in align step. */
+ && new_current >= size /* No overflow in size computation. */
+ && new_current <= buf->__alloc_buffer_end) /* Room in buffer. */
+ {
+ buf->__alloc_buffer_current = new_current;
+ return (void *) aligned;
+ }
+ else
+ {
+ alloc_buffer_mark_failed (buf);
+ return NULL;
+ }
+}
+libc_hidden_def (__libc_alloc_buffer_alloc_array)
diff --git a/malloc/alloc_buffer_allocate.c b/malloc/alloc_buffer_allocate.c
new file mode 100644
index 0000000000..6d24aa6654
--- /dev/null
+++ b/malloc/alloc_buffer_allocate.c
@@ -0,0 +1,36 @@
+/* Allocate a fixed-size allocation buffer using malloc.
+ Copyright (C) 2017-2018 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/>. */
+
+#include <alloc_buffer.h>
+
+#include <stdlib.h>
+
+struct alloc_buffer
+__libc_alloc_buffer_allocate (size_t size, void **pptr)
+{
+ *pptr = malloc (size);
+ if (*pptr == NULL)
+ return (struct alloc_buffer)
+ {
+ .__alloc_buffer_current = __ALLOC_BUFFER_INVALID_POINTER,
+ .__alloc_buffer_end = __ALLOC_BUFFER_INVALID_POINTER
+ };
+ else
+ return alloc_buffer_create (*pptr, size);
+}
+libc_hidden_def (__libc_alloc_buffer_allocate)
diff --git a/malloc/alloc_buffer_copy_bytes.c b/malloc/alloc_buffer_copy_bytes.c
new file mode 100644
index 0000000000..760df931b3
--- /dev/null
+++ b/malloc/alloc_buffer_copy_bytes.c
@@ -0,0 +1,34 @@
+/* Copy an array of bytes into the buffer.
+ Copyright (C) 2017-2018 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/>. */
+
+#include <alloc_buffer.h>
+
+#include <string.h>
+
+/* This function works on a copy of the buffer object, so that it can
+ remain non-addressable in the caller. */
+struct alloc_buffer
+__libc_alloc_buffer_copy_bytes (struct alloc_buffer buf,
+ const void *src, size_t len)
+{
+ void *ptr = alloc_buffer_alloc_bytes (&buf, len);
+ if (ptr != NULL)
+ memcpy (ptr, src, len);
+ return buf;
+}
+libc_hidden_def (__libc_alloc_buffer_copy_bytes)
diff --git a/malloc/alloc_buffer_copy_string.c b/malloc/alloc_buffer_copy_string.c
new file mode 100644
index 0000000000..62113e11ec
--- /dev/null
+++ b/malloc/alloc_buffer_copy_string.c
@@ -0,0 +1,30 @@
+/* Copy a string into the allocation buffer.
+ Copyright (C) 2017-2018 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/>. */
+
+#include <alloc_buffer.h>
+
+#include <string.h>
+
+/* This function works on a copy of the buffer object, so that it can
+ remain non-addressable in the caller. */
+struct alloc_buffer
+__libc_alloc_buffer_copy_string (struct alloc_buffer buf, const char *src)
+{
+ return __libc_alloc_buffer_copy_bytes (buf, src, strlen (src) + 1);
+}
+libc_hidden_def (__libc_alloc_buffer_copy_string)
diff --git a/malloc/alloc_buffer_create_failure.c b/malloc/alloc_buffer_create_failure.c
new file mode 100644
index 0000000000..72765116b6
--- /dev/null
+++ b/malloc/alloc_buffer_create_failure.c
@@ -0,0 +1,31 @@
+/* Terminate the process as the result of an invalid allocation buffer.
+ Copyright (C) 2017-2018 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/>. */
+
+#include <alloc_buffer.h>
+#include <stdio.h>
+
+void
+__libc_alloc_buffer_create_failure (void *start, size_t size)
+{
+ char buf[200];
+ __snprintf (buf, sizeof (buf), "Fatal glibc error: "
+ "invalid allocation buffer of size %zu\n",
+ size);
+ __libc_fatal (buf);
+}
+libc_hidden_def (__libc_alloc_buffer_create_failure)
diff --git a/malloc/arena.c b/malloc/arena.c
index 1edb4d4d35..497ae475e7 100644
--- a/malloc/arena.c
+++ b/malloc/arena.c
@@ -1,5 +1,5 @@
/* Malloc implementation for multiple threads without lock contention.
- Copyright (C) 2001-2016 Free Software Foundation, Inc.
+ Copyright (C) 2001-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Wolfram Gloger <wg@malloc.de>, 2001.
@@ -19,6 +19,11 @@
#include <stdbool.h>
+#if HAVE_TUNABLES
+# define TUNABLE_NAMESPACE malloc
+#endif
+#include <elf/dl-tunables.h>
+
/* Compile-time constants. */
#define HEAP_MIN_SIZE (32 * 1024)
@@ -73,7 +78,7 @@ static __thread mstate thread_arena attribute_tls_model_ie;
members of struct malloc_state objects. No other locks must be
acquired after free_list_lock has been acquired. */
-static mutex_t free_list_lock = _LIBC_LOCK_INITIALIZER;
+__libc_lock_define_initialized (static, free_list_lock);
static size_t narenas = 1;
static mstate free_list;
@@ -89,10 +94,7 @@ static mstate free_list;
acquired, no arena lock must have been acquired, but it is
permitted to acquire arena locks subsequently, while list_lock is
acquired. */
-static mutex_t list_lock = _LIBC_LOCK_INITIALIZER;
-
-/* Mapped memory in non-main arenas (reliable only for NO_THREADS). */
-static unsigned long arena_mem;
+__libc_lock_define_initialized (static, list_lock);
/* Already initialized? */
int __malloc_initialized = -1;
@@ -114,8 +116,8 @@ int __malloc_initialized = -1;
} while (0)
#define arena_lock(ptr, size) do { \
- if (ptr && !arena_is_corrupt (ptr)) \
- (void) mutex_lock (&ptr->mutex); \
+ if (ptr) \
+ __libc_lock_lock (ptr->mutex); \
else \
ptr = arena_get2 ((size), NULL); \
} while (0)
@@ -125,192 +127,71 @@ int __malloc_initialized = -1;
#define heap_for_ptr(ptr) \
((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1)))
#define arena_for_chunk(ptr) \
- (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena)
+ (chunk_main_arena (ptr) ? &main_arena : heap_for_ptr (ptr)->ar_ptr)
/**************************************************************************/
-#ifndef NO_THREADS
-
/* atfork support. */
-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. */
-
-# define ATFORK_ARENA_PTR ((void *) -1)
-
-/* The following hooks are used while the `atfork' handling mechanism
- is active. */
-
-static void *
-malloc_atfork (size_t sz, const void *caller)
-{
- void *victim;
-
- if (thread_arena == ATFORK_ARENA_PTR)
- {
- /* We are the only thread that may allocate at all. */
- if (save_malloc_hook != malloc_check)
- {
- return _int_malloc (&main_arena, sz);
- }
- else
- {
- if (top_check () < 0)
- return 0;
-
- victim = _int_malloc (&main_arena, sz + 1);
- return mem2mem_check (victim, sz);
- }
- }
- else
- {
- /* Suspend the thread until the `atfork' handlers have completed.
- By that time, the hooks will have been reset as well, so that
- mALLOc() can be used again. */
- (void) mutex_lock (&list_lock);
- (void) mutex_unlock (&list_lock);
- return __libc_malloc (sz);
- }
-}
-
-static void
-free_atfork (void *mem, const void *caller)
-{
- mstate ar_ptr;
- mchunkptr p; /* chunk corresponding to mem */
-
- if (mem == 0) /* free(0) has no effect */
- return;
-
- p = mem2chunk (mem); /* do not bother to replicate free_check here */
-
- if (chunk_is_mmapped (p)) /* release mmapped memory. */
- {
- munmap_chunk (p);
- return;
- }
+/* 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. */
- ar_ptr = arena_for_chunk (p);
- _int_free (ar_ptr, p, thread_arena == ATFORK_ARENA_PTR);
-}
-
-
-/* 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). */
-
-static void
-ptmalloc_lock_all (void)
+void
+__malloc_fork_lock_parent (void)
{
- mstate ar_ptr;
-
if (__malloc_initialized < 1)
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))
- {
- if (thread_arena == ATFORK_ARENA_PTR)
- /* This is the same thread which already locks the global list.
- Just bump the counter. */
- goto out;
+ __libc_lock_lock (list_lock);
- /* This thread has to wait its turn. */
- (void) mutex_lock (&list_lock);
- }
- for (ar_ptr = &main_arena;; )
+ for (mstate ar_ptr = &main_arena;; )
{
- (void) mutex_lock (&ar_ptr->mutex);
+ __libc_lock_lock (ar_ptr->mutex);
ar_ptr = ar_ptr->next;
if (ar_ptr == &main_arena)
break;
}
- save_malloc_hook = __malloc_hook;
- save_free_hook = __free_hook;
- __malloc_hook = malloc_atfork;
- __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
- updated. */
- save_arena = thread_arena;
- thread_arena = ATFORK_ARENA_PTR;
-out:
- ++atfork_recursive_cntr;
}
-static void
-ptmalloc_unlock_all (void)
+void
+__malloc_fork_unlock_parent (void)
{
- mstate ar_ptr;
-
if (__malloc_initialized < 1)
return;
- 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;
- for (ar_ptr = &main_arena;; )
+ for (mstate ar_ptr = &main_arena;; )
{
- (void) mutex_unlock (&ar_ptr->mutex);
+ __libc_lock_unlock (ar_ptr->mutex);
ar_ptr = ar_ptr->next;
if (ar_ptr == &main_arena)
break;
}
- (void) mutex_unlock (&list_lock);
+ __libc_lock_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;
-
if (__malloc_initialized < 1)
return;
- 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
+ /* Push all arenas to the free list, except thread_arena, which is
attached to the current thread. */
- mutex_init (&free_list_lock);
- if (save_arena != NULL)
- ((mstate) save_arena)->attached_threads = 1;
+ __libc_lock_init (free_list_lock);
+ if (thread_arena != NULL)
+ thread_arena->attached_threads = 1;
free_list = NULL;
- for (ar_ptr = &main_arena;; )
+ for (mstate ar_ptr = &main_arena;; )
{
- mutex_init (&ar_ptr->mutex);
- if (ar_ptr != save_arena)
+ __libc_lock_init (ar_ptr->mutex);
+ if (ar_ptr != thread_arena)
{
/* This arena is no longer attached to any thread. */
ar_ptr->attached_threads = 0;
@@ -322,22 +203,46 @@ ptmalloc_unlock_all2 (void)
break;
}
- mutex_init (&list_lock);
- atfork_recursive_cntr = 0;
+ __libc_lock_init (list_lock);
}
-# else
+#if HAVE_TUNABLES
+static inline int do_set_mallopt_check (int32_t value);
+void
+TUNABLE_CALLBACK (set_mallopt_check) (tunable_val_t *valp)
+{
+ int32_t value = (int32_t) valp->numval;
+ if (value != 0)
+ __malloc_check_init ();
+}
-# define ptmalloc_unlock_all2 ptmalloc_unlock_all
-# endif
-#endif /* !NO_THREADS */
+# define TUNABLE_CALLBACK_FNDECL(__name, __type) \
+static inline int do_ ## __name (__type value); \
+void \
+TUNABLE_CALLBACK (__name) (tunable_val_t *valp) \
+{ \
+ __type value = (__type) (valp)->numval; \
+ do_ ## __name (value); \
+}
+TUNABLE_CALLBACK_FNDECL (set_mmap_threshold, size_t)
+TUNABLE_CALLBACK_FNDECL (set_mmaps_max, int32_t)
+TUNABLE_CALLBACK_FNDECL (set_top_pad, size_t)
+TUNABLE_CALLBACK_FNDECL (set_perturb_byte, int32_t)
+TUNABLE_CALLBACK_FNDECL (set_trim_threshold, size_t)
+TUNABLE_CALLBACK_FNDECL (set_arena_max, size_t)
+TUNABLE_CALLBACK_FNDECL (set_arena_test, size_t)
+#if USE_TCACHE
+TUNABLE_CALLBACK_FNDECL (set_tcache_max, size_t)
+TUNABLE_CALLBACK_FNDECL (set_tcache_count, size_t)
+TUNABLE_CALLBACK_FNDECL (set_tcache_unsorted_limit, size_t)
+#endif
+#else
/* Initialization routine. */
#include <string.h>
extern char **_environ;
static char *
-internal_function
next_env_entry (char ***position)
{
char **current = *position;
@@ -366,6 +271,7 @@ next_env_entry (char ***position)
return result;
}
+#endif
#ifdef SHARED
@@ -400,7 +306,25 @@ ptmalloc_init (void)
#endif
thread_arena = &main_arena;
- thread_atfork (ptmalloc_lock_all, ptmalloc_unlock_all, ptmalloc_unlock_all2);
+
+ malloc_init_state (&main_arena);
+
+#if HAVE_TUNABLES
+ TUNABLE_GET (check, int32_t, TUNABLE_CALLBACK (set_mallopt_check));
+ TUNABLE_GET (top_pad, size_t, TUNABLE_CALLBACK (set_top_pad));
+ TUNABLE_GET (perturb, int32_t, TUNABLE_CALLBACK (set_perturb_byte));
+ TUNABLE_GET (mmap_threshold, size_t, TUNABLE_CALLBACK (set_mmap_threshold));
+ TUNABLE_GET (trim_threshold, size_t, TUNABLE_CALLBACK (set_trim_threshold));
+ TUNABLE_GET (mmap_max, int32_t, TUNABLE_CALLBACK (set_mmaps_max));
+ TUNABLE_GET (arena_max, size_t, TUNABLE_CALLBACK (set_arena_max));
+ TUNABLE_GET (arena_test, size_t, TUNABLE_CALLBACK (set_arena_test));
+# if USE_TCACHE
+ TUNABLE_GET (tcache_max, size_t, TUNABLE_CALLBACK (set_tcache_max));
+ TUNABLE_GET (tcache_count, size_t, TUNABLE_CALLBACK (set_tcache_count));
+ TUNABLE_GET (tcache_unsorted_limit, size_t,
+ TUNABLE_CALLBACK (set_tcache_unsorted_limit));
+# endif
+#else
const char *s = NULL;
if (__glibc_likely (_environ != NULL))
{
@@ -463,26 +387,18 @@ ptmalloc_init (void)
}
}
}
- if (s && s[0])
- {
- __libc_mallopt (M_CHECK_ACTION, (int) (s[0] - '0'));
- if (check_action != 0)
- __malloc_check_init ();
- }
+ if (s && s[0] != '\0' && s[0] != '0')
+ __malloc_check_init ();
+#endif
+
+#if HAVE_MALLOC_INIT_HOOK
void (*hook) (void) = atomic_forced_read (__malloc_initialize_hook);
if (hook != NULL)
(*hook)();
+#endif
__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
@@ -533,7 +449,6 @@ static char *aligned_heap_area;
of the page size. */
static heap_info *
-internal_function
new_heap (size_t size, size_t top_pad)
{
size_t pagesize = GLRO (dl_pagesize);
@@ -677,7 +592,6 @@ shrink_heap (heap_info *h, long diff)
} while (0)
static int
-internal_function
heap_trim (heap_info *heap, size_t pad)
{
mstate ar_ptr = heap->ar_ptr;
@@ -695,17 +609,16 @@ heap_trim (heap_info *heap, size_t pad)
/* fencepost must be properly aligned. */
misalign = ((long) p) & MALLOC_ALIGN_MASK;
p = chunk_at_offset (prev_heap, prev_size - misalign);
- assert (p->size == (0 | PREV_INUSE)); /* must be fencepost */
+ assert (chunksize_nomask (p) == (0 | PREV_INUSE)); /* must be fencepost */
p = prev_chunk (p);
new_size = chunksize (p) + (MINSIZE - 2 * SIZE_SZ) + misalign;
assert (new_size > 0 && new_size < (long) (2 * MINSIZE));
if (!prev_inuse (p))
- new_size += p->prev_size;
+ new_size += prev_size (p);
assert (new_size > 0 && new_size < HEAP_MAX_SIZE);
if (new_size + (HEAP_MAX_SIZE - prev_heap->size) < pad + MINSIZE + pagesz)
break;
ar_ptr->system_mem -= heap->size;
- arena_mem -= heap->size;
LIBC_PROBE (memory_heap_free, 2, heap, heap->size);
delete_heap (heap);
heap = prev_heap;
@@ -743,7 +656,6 @@ heap_trim (heap_info *heap, size_t pad)
return 0;
ar_ptr->system_mem -= extra;
- arena_mem -= extra;
/* Success. Adjust top accordingly. */
set_head (top_chunk, (top_size - extra) | PREV_INUSE);
@@ -793,7 +705,6 @@ _int_new_arena (size_t size)
a->attached_threads = 1;
/*a->next = NULL;*/
a->system_mem = a->max_system_mem = h->size;
- arena_mem += h->size;
/* Set up the top chunk, with proper alignment. */
ptr = (char *) (a + 1);
@@ -806,9 +717,9 @@ _int_new_arena (size_t size)
LIBC_PROBE (memory_arena_new, 2, a, size);
mstate replaced_arena = thread_arena;
thread_arena = a;
- mutex_init (&a->mutex);
+ __libc_lock_init (a->mutex);
- (void) mutex_lock (&list_lock);
+ __libc_lock_lock (list_lock);
/* Add the new arena to the global list. */
a->next = main_arena.next;
@@ -818,11 +729,11 @@ _int_new_arena (size_t size)
atomic_write_barrier ();
main_arena.next = a;
- (void) mutex_unlock (&list_lock);
+ __libc_lock_unlock (list_lock);
- (void) mutex_lock (&free_list_lock);
+ __libc_lock_lock (free_list_lock);
detach_arena (replaced_arena);
- (void) mutex_unlock (&free_list_lock);
+ __libc_lock_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
@@ -831,16 +742,16 @@ _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);
+ __libc_lock_lock (a->mutex);
return a;
}
-/* Remove an arena from free_list. The arena may be in use because it
- was attached concurrently to a thread by reused_arena below. */
+/* Remove an arena from free_list. */
static mstate
get_free_list (void)
{
@@ -848,23 +759,24 @@ get_free_list (void)
mstate result = free_list;
if (result != NULL)
{
- (void) mutex_lock (&free_list_lock);
+ __libc_lock_lock (free_list_lock);
result = free_list;
if (result != NULL)
{
free_list = result->next_free;
/* The arena will be attached to this thread. */
- ++result->attached_threads;
+ assert (result->attached_threads == 0);
+ result->attached_threads = 1;
detach_arena (replaced_arena);
}
- (void) mutex_unlock (&free_list_lock);
+ __libc_lock_unlock (free_list_lock);
if (result != NULL)
{
LIBC_PROBE (memory_arena_reuse_free_list, 1, result);
- (void) mutex_lock (&result->mutex);
+ __libc_lock_lock (result->mutex);
thread_arena = result;
}
}
@@ -872,6 +784,26 @@ get_free_list (void)
return result;
}
+/* Remove the arena from the free list (if it is present).
+ free_list_lock must have been acquired by the caller. */
+static void
+remove_from_free_list (mstate arena)
+{
+ mstate *previous = &free_list;
+ for (mstate p = free_list; p != NULL; p = p->next_free)
+ {
+ assert (p->attached_threads == 0);
+ if (p == arena)
+ {
+ /* Remove the requested arena from the list. */
+ *previous = p->next_free;
+ break;
+ }
+ else
+ previous = &p->next_free;
+ }
+}
+
/* Lock and return an arena that can be reused for memory allocation.
Avoid AVOID_ARENA as we have already failed to allocate memory in
it and it is currently locked. */
@@ -889,7 +821,7 @@ reused_arena (mstate avoid_arena)
result = next_to_use;
do
{
- if (!arena_is_corrupt (result) && !mutex_trylock (&result->mutex))
+ if (!__libc_lock_trylock (result->mutex))
goto out;
/* FIXME: This is a data race, see _int_new_arena. */
@@ -902,34 +834,31 @@ reused_arena (mstate avoid_arena)
if (result == avoid_arena)
result = result->next;
- /* Make sure that the arena we get is not corrupted. */
- mstate begin = result;
- while (arena_is_corrupt (result) || result == avoid_arena)
- {
- result = result->next;
- if (result == begin)
- break;
- }
-
- /* We could not find any arena that was either not corrupted or not the one
- we wanted to avoid. */
- if (result == begin || result == avoid_arena)
- return NULL;
-
/* No arena available without contention. Wait for the next in line. */
LIBC_PROBE (memory_arena_reuse_wait, 3, &result->mutex, result, avoid_arena);
- (void) mutex_lock (&result->mutex);
+ __libc_lock_lock (result->mutex);
out:
- /* Attach the arena to the current thread. Note that we may have
- selected an arena which was on free_list. */
+ /* Attach the arena to the current thread. */
{
/* Update the arena thread attachment counters. */
mstate replaced_arena = thread_arena;
- (void) mutex_lock (&free_list_lock);
+ __libc_lock_lock (free_list_lock);
detach_arena (replaced_arena);
+
+ /* We may have picked up an arena on the free list. We need to
+ preserve the invariant that no arena on the free list has a
+ positive attached_threads counter (otherwise,
+ arena_thread_freeres cannot use the counter to determine if the
+ arena needs to be put on the free list). We unconditionally
+ remove the selected arena from the free list. The caller of
+ reused_arena checked the free list and observed it to be empty,
+ so the list is very short. */
+ remove_from_free_list (result);
+
++result->attached_threads;
- (void) mutex_unlock (&free_list_lock);
+
+ __libc_lock_unlock (free_list_lock);
}
LIBC_PROBE (memory_arena_reuse, 2, result, avoid_arena);
@@ -940,7 +869,6 @@ out:
}
static mstate
-internal_function
arena_get2 (size_t size, mstate avoid_arena)
{
mstate a;
@@ -1000,32 +928,33 @@ arena_get_retry (mstate ar_ptr, size_t bytes)
LIBC_PROBE (memory_arena_retry, 2, bytes, ar_ptr);
if (ar_ptr != &main_arena)
{
- (void) mutex_unlock (&ar_ptr->mutex);
- /* Don't touch the main arena if it is corrupt. */
- if (arena_is_corrupt (&main_arena))
- return NULL;
-
+ __libc_lock_unlock (ar_ptr->mutex);
ar_ptr = &main_arena;
- (void) mutex_lock (&ar_ptr->mutex);
+ __libc_lock_lock (ar_ptr->mutex);
}
else
{
- (void) mutex_unlock (&ar_ptr->mutex);
+ __libc_lock_unlock (ar_ptr->mutex);
ar_ptr = arena_get2 (bytes, ar_ptr);
}
return ar_ptr;
}
-static void __attribute__ ((section ("__libc_thread_freeres_fn")))
-arena_thread_freeres (void)
+void
+__malloc_arena_thread_freeres (void)
{
+ /* Shut down the thread cache first. This could deallocate data for
+ the thread arena, so do this before we put the arena on the free
+ list. */
+ tcache_thread_shutdown ();
+
mstate a = thread_arena;
thread_arena = NULL;
if (a != NULL)
{
- (void) mutex_lock (&free_list_lock);
+ __libc_lock_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);
@@ -1034,10 +963,9 @@ arena_thread_freeres (void)
a->next_free = free_list;
free_list = a;
}
- (void) mutex_unlock (&free_list_lock);
+ __libc_lock_unlock (free_list_lock);
}
}
-text_set_element (__libc_thread_subfreeres, arena_thread_freeres);
/*
* Local variables:
diff --git a/malloc/dynarray-skeleton.c b/malloc/dynarray-skeleton.c
new file mode 100644
index 0000000000..5ab4a199a4
--- /dev/null
+++ b/malloc/dynarray-skeleton.c
@@ -0,0 +1,521 @@
+/* Type-safe arrays which grow dynamically.
+ Copyright (C) 2017-2018 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/>. */
+
+/* Pre-processor macros which act as parameters:
+
+ DYNARRAY_STRUCT
+ The struct tag of dynamic array to be defined.
+ DYNARRAY_ELEMENT
+ The type name of the element type. Elements are copied
+ as if by memcpy, and can change address as the dynamic
+ array grows.
+ DYNARRAY_PREFIX
+ The prefix of the functions which are defined.
+
+ The following parameters are optional:
+
+ DYNARRAY_ELEMENT_FREE
+ DYNARRAY_ELEMENT_FREE (E) is evaluated to deallocate the
+ contents of elements. E is of type DYNARRAY_ELEMENT *.
+ DYNARRAY_ELEMENT_INIT
+ DYNARRAY_ELEMENT_INIT (E) is evaluated to initialize a new
+ element. E is of type DYNARRAY_ELEMENT *.
+ If DYNARRAY_ELEMENT_FREE but not DYNARRAY_ELEMENT_INIT is
+ defined, new elements are automatically zero-initialized.
+ Otherwise, new elements have undefined contents.
+ DYNARRAY_INITIAL_SIZE
+ The size of the statically allocated array (default:
+ at least 2, more elements if they fit into 128 bytes).
+ Must be a preprocessor constant. If DYNARRAY_INITIAL_SIZE is 0,
+ there is no statically allocated array at, and all non-empty
+ arrays are heap-allocated.
+ DYNARRAY_FINAL_TYPE
+ The name of the type which holds the final array. If not
+ defined, is PREFIX##finalize not provided. DYNARRAY_FINAL_TYPE
+ must be a struct type, with members of type DYNARRAY_ELEMENT and
+ size_t at the start (in this order).
+
+ These macros are undefined after this header file has been
+ included.
+
+ The following types are provided (their members are private to the
+ dynarray implementation):
+
+ struct DYNARRAY_STRUCT
+
+ The following functions are provided:
+
+ void DYNARRAY_PREFIX##init (struct DYNARRAY_STRUCT *);
+ void DYNARRAY_PREFIX##free (struct DYNARRAY_STRUCT *);
+ bool DYNARRAY_PREFIX##has_failed (const struct DYNARRAY_STRUCT *);
+ void DYNARRAY_PREFIX##mark_failed (struct DYNARRAY_STRUCT *);
+ size_t DYNARRAY_PREFIX##size (const struct DYNARRAY_STRUCT *);
+ DYNARRAY_ELEMENT *DYNARRAY_PREFIX##begin (const struct DYNARRAY_STRUCT *);
+ DYNARRAY_ELEMENT *DYNARRAY_PREFIX##end (const struct DYNARRAY_STRUCT *);
+ DYNARRAY_ELEMENT *DYNARRAY_PREFIX##at (struct DYNARRAY_STRUCT *, size_t);
+ void DYNARRAY_PREFIX##add (struct DYNARRAY_STRUCT *, DYNARRAY_ELEMENT);
+ DYNARRAY_ELEMENT *DYNARRAY_PREFIX##emplace (struct DYNARRAY_STRUCT *);
+ bool DYNARRAY_PREFIX##resize (struct DYNARRAY_STRUCT *, size_t);
+ void DYNARRAY_PREFIX##remove_last (struct DYNARRAY_STRUCT *);
+ void DYNARRAY_PREFIX##clear (struct DYNARRAY_STRUCT *);
+
+ The following functions are provided are provided if the
+ prerequisites are met:
+
+ bool DYNARRAY_PREFIX##finalize (struct DYNARRAY_STRUCT *,
+ DYNARRAY_FINAL_TYPE *);
+ (if DYNARRAY_FINAL_TYPE is defined)
+ DYNARRAY_ELEMENT *DYNARRAY_PREFIX##finalize (struct DYNARRAY_STRUCT *,
+ size_t *);
+ (if DYNARRAY_FINAL_TYPE is not defined)
+*/
+
+#include <malloc/dynarray.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef DYNARRAY_STRUCT
+# error "DYNARRAY_STRUCT must be defined"
+#endif
+
+#ifndef DYNARRAY_ELEMENT
+# error "DYNARRAY_ELEMENT must be defined"
+#endif
+
+#ifndef DYNARRAY_PREFIX
+# error "DYNARRAY_PREFIX must be defined"
+#endif
+
+#ifdef DYNARRAY_INITIAL_SIZE
+# if DYNARRAY_INITIAL_SIZE < 0
+# error "DYNARRAY_INITIAL_SIZE must be non-negative"
+# endif
+# if DYNARRAY_INITIAL_SIZE > 0
+# define DYNARRAY_HAVE_SCRATCH 1
+# else
+# define DYNARRAY_HAVE_SCRATCH 0
+# endif
+#else
+/* Provide a reasonable default which limits the size of
+ DYNARRAY_STRUCT. */
+# define DYNARRAY_INITIAL_SIZE \
+ (sizeof (DYNARRAY_ELEMENT) > 64 ? 2 : 128 / sizeof (DYNARRAY_ELEMENT))
+# define DYNARRAY_HAVE_SCRATCH 1
+#endif
+
+/* Public type definitions. */
+
+/* All fields of this struct are private to the implementation. */
+struct DYNARRAY_STRUCT
+{
+ union
+ {
+ struct dynarray_header dynarray_abstract;
+ struct
+ {
+ /* These fields must match struct dynarray_header. */
+ size_t used;
+ size_t allocated;
+ DYNARRAY_ELEMENT *array;
+ } dynarray_header;
+ };
+
+#if DYNARRAY_HAVE_SCRATCH
+ /* Initial inline allocation. */
+ DYNARRAY_ELEMENT scratch[DYNARRAY_INITIAL_SIZE];
+#endif
+};
+
+/* Internal use only: Helper macros. */
+
+/* Ensure macro-expansion of DYNARRAY_PREFIX. */
+#define DYNARRAY_CONCAT0(prefix, name) prefix##name
+#define DYNARRAY_CONCAT1(prefix, name) DYNARRAY_CONCAT0(prefix, name)
+#define DYNARRAY_NAME(name) DYNARRAY_CONCAT1(DYNARRAY_PREFIX, name)
+
+/* Address of the scratch buffer if any. */
+#if DYNARRAY_HAVE_SCRATCH
+# define DYNARRAY_SCRATCH(list) (list)->scratch
+#else
+# define DYNARRAY_SCRATCH(list) NULL
+#endif
+
+/* Internal use only: Helper functions. */
+
+/* Internal function. Call DYNARRAY_ELEMENT_FREE with the array
+ elements. Name mangling needed due to the DYNARRAY_ELEMENT_FREE
+ macro expansion. */
+static inline void
+DYNARRAY_NAME (free__elements__) (DYNARRAY_ELEMENT *__dynarray_array,
+ size_t __dynarray_used)
+{
+#ifdef DYNARRAY_ELEMENT_FREE
+ for (size_t __dynarray_i = 0; __dynarray_i < __dynarray_used; ++__dynarray_i)
+ DYNARRAY_ELEMENT_FREE (&__dynarray_array[__dynarray_i]);
+#endif /* DYNARRAY_ELEMENT_FREE */
+}
+
+/* Internal function. Free the non-scratch array allocation. */
+static inline void
+DYNARRAY_NAME (free__array__) (struct DYNARRAY_STRUCT *list)
+{
+#if DYNARRAY_HAVE_SCRATCH
+ if (list->dynarray_header.array != list->scratch)
+ free (list->dynarray_header.array);
+#else
+ free (list->dynarray_header.array);
+#endif
+}
+
+/* Public functions. */
+
+/* Initialize a dynamic array object. This must be called before any
+ use of the object. */
+__attribute__ ((nonnull (1)))
+static void
+DYNARRAY_NAME (init) (struct DYNARRAY_STRUCT *list)
+{
+ list->dynarray_header.used = 0;
+ list->dynarray_header.allocated = DYNARRAY_INITIAL_SIZE;
+ list->dynarray_header.array = DYNARRAY_SCRATCH (list);
+}
+
+/* Deallocate the dynamic array and its elements. */
+__attribute__ ((unused, nonnull (1)))
+static void
+DYNARRAY_NAME (free) (struct DYNARRAY_STRUCT *list)
+{
+ DYNARRAY_NAME (free__elements__)
+ (list->dynarray_header.array, list->dynarray_header.used);
+ DYNARRAY_NAME (free__array__) (list);
+ DYNARRAY_NAME (init) (list);
+}
+
+/* Return true if the dynamic array is in an error state. */
+__attribute__ ((nonnull (1)))
+static inline bool
+DYNARRAY_NAME (has_failed) (const struct DYNARRAY_STRUCT *list)
+{
+ return list->dynarray_header.allocated == __dynarray_error_marker ();
+}
+
+/* Mark the dynamic array as failed. All elements are deallocated as
+ a side effect. */
+__attribute__ ((nonnull (1)))
+static void
+DYNARRAY_NAME (mark_failed) (struct DYNARRAY_STRUCT *list)
+{
+ DYNARRAY_NAME (free__elements__)
+ (list->dynarray_header.array, list->dynarray_header.used);
+ DYNARRAY_NAME (free__array__) (list);
+ list->dynarray_header.array = DYNARRAY_SCRATCH (list);
+ list->dynarray_header.used = 0;
+ list->dynarray_header.allocated = __dynarray_error_marker ();
+}
+
+/* Return the number of elements which have been added to the dynamic
+ array. */
+__attribute__ ((nonnull (1)))
+static inline size_t
+DYNARRAY_NAME (size) (const struct DYNARRAY_STRUCT *list)
+{
+ return list->dynarray_header.used;
+}
+
+/* Return a pointer to the array element at INDEX. Terminate the
+ process if INDEX is out of bounds. */
+__attribute__ ((nonnull (1)))
+static inline DYNARRAY_ELEMENT *
+DYNARRAY_NAME (at) (struct DYNARRAY_STRUCT *list, size_t index)
+{
+ if (__glibc_unlikely (index >= DYNARRAY_NAME (size) (list)))
+ __libc_dynarray_at_failure (DYNARRAY_NAME (size) (list), index);
+ return list->dynarray_header.array + index;
+}
+
+/* Return a pointer to the first array element, if any. For a
+ zero-length array, the pointer can be NULL even though the dynamic
+ array has not entered the failure state. */
+__attribute__ ((nonnull (1)))
+static inline DYNARRAY_ELEMENT *
+DYNARRAY_NAME (begin) (struct DYNARRAY_STRUCT *list)
+{
+ return list->dynarray_header.array;
+}
+
+/* Return a pointer one element past the last array element. For a
+ zero-length array, the pointer can be NULL even though the dynamic
+ array has not entered the failure state. */
+__attribute__ ((nonnull (1)))
+static inline DYNARRAY_ELEMENT *
+DYNARRAY_NAME (end) (struct DYNARRAY_STRUCT *list)
+{
+ return list->dynarray_header.array + list->dynarray_header.used;
+}
+
+/* Internal function. Slow path for the add function below. */
+static void
+DYNARRAY_NAME (add__) (struct DYNARRAY_STRUCT *list, DYNARRAY_ELEMENT item)
+{
+ if (__glibc_unlikely
+ (!__libc_dynarray_emplace_enlarge (&list->dynarray_abstract,
+ DYNARRAY_SCRATCH (list),
+ sizeof (DYNARRAY_ELEMENT))))
+ {
+ DYNARRAY_NAME (mark_failed) (list);
+ return;
+ }
+
+ /* Copy the new element and increase the array length. */
+ list->dynarray_header.array[list->dynarray_header.used++] = item;
+}
+
+/* Add ITEM at the end of the array, enlarging it by one element.
+ Mark *LIST as failed if the dynamic array allocation size cannot be
+ increased. */
+__attribute__ ((unused, nonnull (1)))
+static inline void
+DYNARRAY_NAME (add) (struct DYNARRAY_STRUCT *list, DYNARRAY_ELEMENT item)
+{
+ /* Do nothing in case of previous error. */
+ if (DYNARRAY_NAME (has_failed) (list))
+ return;
+
+ /* Enlarge the array if necessary. */
+ if (__glibc_unlikely (list->dynarray_header.used
+ == list->dynarray_header.allocated))
+ {
+ DYNARRAY_NAME (add__) (list, item);
+ return;
+ }
+
+ /* Copy the new element and increase the array length. */
+ list->dynarray_header.array[list->dynarray_header.used++] = item;
+}
+
+/* Internal function. Building block for the emplace functions below.
+ Assumes space for one more element in *LIST. */
+static inline DYNARRAY_ELEMENT *
+DYNARRAY_NAME (emplace__tail__) (struct DYNARRAY_STRUCT *list)
+{
+ DYNARRAY_ELEMENT *result
+ = &list->dynarray_header.array[list->dynarray_header.used];
+ ++list->dynarray_header.used;
+#if defined (DYNARRAY_ELEMENT_INIT)
+ DYNARRAY_ELEMENT_INIT (result);
+#elif defined (DYNARRAY_ELEMENT_FREE)
+ memset (result, 0, sizeof (*result));
+#endif
+ return result;
+}
+
+/* Internal function. Slow path for the emplace function below. */
+static DYNARRAY_ELEMENT *
+DYNARRAY_NAME (emplace__) (struct DYNARRAY_STRUCT *list)
+{
+ if (__glibc_unlikely
+ (!__libc_dynarray_emplace_enlarge (&list->dynarray_abstract,
+ DYNARRAY_SCRATCH (list),
+ sizeof (DYNARRAY_ELEMENT))))
+ {
+ DYNARRAY_NAME (mark_failed) (list);
+ return NULL;
+ }
+ return DYNARRAY_NAME (emplace__tail__) (list);
+}
+
+/* Allocate a place for a new element in *LIST and return a pointer to
+ it. The pointer can be NULL if the dynamic array cannot be
+ enlarged due to a memory allocation failure. */
+__attribute__ ((unused, warn_unused_result, nonnull (1)))
+static
+/* Avoid inlining with the larger initialization code. */
+#if !(defined (DYNARRAY_ELEMENT_INIT) || defined (DYNARRAY_ELEMENT_FREE))
+inline
+#endif
+DYNARRAY_ELEMENT *
+DYNARRAY_NAME (emplace) (struct DYNARRAY_STRUCT *list)
+{
+ /* Do nothing in case of previous error. */
+ if (DYNARRAY_NAME (has_failed) (list))
+ return NULL;
+
+ /* Enlarge the array if necessary. */
+ if (__glibc_unlikely (list->dynarray_header.used
+ == list->dynarray_header.allocated))
+ return (DYNARRAY_NAME (emplace__) (list));
+ return DYNARRAY_NAME (emplace__tail__) (list);
+}
+
+/* Change the size of *LIST to SIZE. If SIZE is larger than the
+ existing size, new elements are added (which can be initialized).
+ Otherwise, the list is truncated, and elements are freed. Return
+ false on memory allocation failure (and mark *LIST as failed). */
+__attribute__ ((unused, nonnull (1)))
+static bool
+DYNARRAY_NAME (resize) (struct DYNARRAY_STRUCT *list, size_t size)
+{
+ if (size > list->dynarray_header.used)
+ {
+ bool ok;
+#if defined (DYNARRAY_ELEMENT_INIT)
+ /* The new elements have to be initialized. */
+ size_t old_size = list->dynarray_header.used;
+ ok = __libc_dynarray_resize (&list->dynarray_abstract,
+ size, DYNARRAY_SCRATCH (list),
+ sizeof (DYNARRAY_ELEMENT));
+ if (ok)
+ for (size_t i = old_size; i < size; ++i)
+ {
+ DYNARRAY_ELEMENT_INIT (&list->dynarray_header.array[i]);
+ }
+#elif defined (DYNARRAY_ELEMENT_FREE)
+ /* Zero initialization is needed so that the elements can be
+ safely freed. */
+ ok = __libc_dynarray_resize_clear
+ (&list->dynarray_abstract, size,
+ DYNARRAY_SCRATCH (list), sizeof (DYNARRAY_ELEMENT));
+#else
+ ok = __libc_dynarray_resize (&list->dynarray_abstract,
+ size, DYNARRAY_SCRATCH (list),
+ sizeof (DYNARRAY_ELEMENT));
+#endif
+ if (__glibc_unlikely (!ok))
+ DYNARRAY_NAME (mark_failed) (list);
+ return ok;
+ }
+ else
+ {
+ /* The list has shrunk in size. Free the removed elements. */
+ DYNARRAY_NAME (free__elements__)
+ (list->dynarray_header.array + size,
+ list->dynarray_header.used - size);
+ list->dynarray_header.used = size;
+ return true;
+ }
+}
+
+/* Remove the last element of LIST if it is present. */
+__attribute__ ((unused, nonnull (1)))
+static void
+DYNARRAY_NAME (remove_last) (struct DYNARRAY_STRUCT *list)
+{
+ /* used > 0 implies that the array is the non-failed state. */
+ if (list->dynarray_header.used > 0)
+ {
+ size_t new_length = list->dynarray_header.used - 1;
+#ifdef DYNARRAY_ELEMENT_FREE
+ DYNARRAY_ELEMENT_FREE (&list->dynarray_header.array[new_length]);
+#endif
+ list->dynarray_header.used = new_length;
+ }
+}
+
+/* Remove all elements from the list. The elements are freed, but the
+ list itself is not. */
+__attribute__ ((unused, nonnull (1)))
+static void
+DYNARRAY_NAME (clear) (struct DYNARRAY_STRUCT *list)
+{
+ /* free__elements__ does nothing if the list is in the failed
+ state. */
+ DYNARRAY_NAME (free__elements__)
+ (list->dynarray_header.array, list->dynarray_header.used);
+ list->dynarray_header.used = 0;
+}
+
+#ifdef DYNARRAY_FINAL_TYPE
+/* Transfer the dynamic array to a permanent location at *RESULT.
+ Returns true on success on false on allocation failure. In either
+ case, *LIST is re-initialized and can be reused. A NULL pointer is
+ stored in *RESULT if LIST refers to an empty list. On success, the
+ pointer in *RESULT is heap-allocated and must be deallocated using
+ free. */
+__attribute__ ((unused, warn_unused_result, nonnull (1, 2)))
+static bool
+DYNARRAY_NAME (finalize) (struct DYNARRAY_STRUCT *list,
+ DYNARRAY_FINAL_TYPE *result)
+{
+ struct dynarray_finalize_result res;
+ if (__libc_dynarray_finalize (&list->dynarray_abstract,
+ DYNARRAY_SCRATCH (list),
+ sizeof (DYNARRAY_ELEMENT), &res))
+ {
+ /* On success, the result owns all the data. */
+ DYNARRAY_NAME (init) (list);
+ *result = (DYNARRAY_FINAL_TYPE) { res.array, res.length };
+ return true;
+ }
+ else
+ {
+ /* On error, we need to free all data. */
+ DYNARRAY_NAME (free) (list);
+ errno = ENOMEM;
+ return false;
+ }
+}
+#else /* !DYNARRAY_FINAL_TYPE */
+/* Transfer the dynamic array to a heap-allocated array and return a
+ pointer to it. The pointer is NULL if memory allocation fails, or
+ if the array is empty, so this function should be used only for
+ arrays which are known not be empty (usually because they always
+ have a sentinel at the end). If LENGTHP is not NULL, the array
+ length is written to *LENGTHP. *LIST is re-initialized and can be
+ reused. */
+__attribute__ ((unused, warn_unused_result, nonnull (1)))
+static DYNARRAY_ELEMENT *
+DYNARRAY_NAME (finalize) (struct DYNARRAY_STRUCT *list, size_t *lengthp)
+{
+ struct dynarray_finalize_result res;
+ if (__libc_dynarray_finalize (&list->dynarray_abstract,
+ DYNARRAY_SCRATCH (list),
+ sizeof (DYNARRAY_ELEMENT), &res))
+ {
+ /* On success, the result owns all the data. */
+ DYNARRAY_NAME (init) (list);
+ if (lengthp != NULL)
+ *lengthp = res.length;
+ return res.array;
+ }
+ else
+ {
+ /* On error, we need to free all data. */
+ DYNARRAY_NAME (free) (list);
+ errno = ENOMEM;
+ return NULL;
+ }
+}
+#endif /* !DYNARRAY_FINAL_TYPE */
+
+/* Undo macro definitions. */
+
+#undef DYNARRAY_CONCAT0
+#undef DYNARRAY_CONCAT1
+#undef DYNARRAY_NAME
+#undef DYNARRAY_SCRATCH
+#undef DYNARRAY_HAVE_SCRATCH
+
+#undef DYNARRAY_STRUCT
+#undef DYNARRAY_ELEMENT
+#undef DYNARRAY_PREFIX
+#undef DYNARRAY_ELEMENT_FREE
+#undef DYNARRAY_ELEMENT_INIT
+#undef DYNARRAY_INITIAL_SIZE
+#undef DYNARRAY_FINAL_TYPE
diff --git a/malloc/dynarray.h b/malloc/dynarray.h
new file mode 100644
index 0000000000..0b171da78c
--- /dev/null
+++ b/malloc/dynarray.h
@@ -0,0 +1,179 @@
+/* Type-safe arrays which grow dynamically. Shared definitions.
+ Copyright (C) 2017-2018 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/>. */
+
+/* To use the dynarray facility, you need to include
+ <malloc/dynarray-skeleton.c> and define the parameter macros
+ documented in that file.
+
+ A minimal example which provides a growing list of integers can be
+ defined like this:
+
+ struct int_array
+ {
+ // Pointer to result array followed by its length,
+ // as required by DYNARRAY_FINAL_TYPE.
+ int *array;
+ size_t length;
+ };
+
+ #define DYNARRAY_STRUCT dynarray_int
+ #define DYNARRAY_ELEMENT int
+ #define DYNARRAY_PREFIX dynarray_int_
+ #define DYNARRAY_FINAL_TYPE struct int_array
+ #include <malloc/dynarray-skeleton.c>
+
+ To create a three-element array with elements 1, 2, 3, use this
+ code:
+
+ struct dynarray_int dyn;
+ dynarray_int_init (&dyn);
+ for (int i = 1; i <= 3; ++i)
+ {
+ int *place = dynarray_int_emplace (&dyn);
+ assert (place != NULL);
+ *place = i;
+ }
+ struct int_array result;
+ bool ok = dynarray_int_finalize (&dyn, &result);
+ assert (ok);
+ assert (result.length == 3);
+ assert (result.array[0] == 1);
+ assert (result.array[1] == 2);
+ assert (result.array[2] == 3);
+ free (result.array);
+
+ If the elements contain resources which must be freed, define
+ DYNARRAY_ELEMENT_FREE appropriately, like this:
+
+ struct str_array
+ {
+ char **array;
+ size_t length;
+ };
+
+ #define DYNARRAY_STRUCT dynarray_str
+ #define DYNARRAY_ELEMENT char *
+ #define DYNARRAY_ELEMENT_FREE(ptr) free (*ptr)
+ #define DYNARRAY_PREFIX dynarray_str_
+ #define DYNARRAY_FINAL_TYPE struct str_array
+ #include <malloc/dynarray-skeleton.c>
+
+ Compared to scratch buffers, dynamic arrays have the following
+ features:
+
+ - They have an element type, and are not just an untyped buffer of
+ bytes.
+
+ - When growing, previously stored elements are preserved. (It is
+ expected that scratch_buffer_grow_preserve and
+ scratch_buffer_set_array_size eventually go away because all
+ current users are moved to dynamic arrays.)
+
+ - Scratch buffers have a more aggressive growth policy because
+ growing them typically means a retry of an operation (across an
+ NSS service module boundary), which is expensive.
+
+ - For the same reason, scratch buffers have a much larger initial
+ stack allocation. */
+
+#ifndef _DYNARRAY_H
+#define _DYNARRAY_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
+struct dynarray_header
+{
+ size_t used;
+ size_t allocated;
+ void *array;
+};
+
+/* Marker used in the allocated member to indicate that an error was
+ encountered. */
+static inline size_t
+__dynarray_error_marker (void)
+{
+ return -1;
+}
+
+/* Internal function. See the has_failed function in
+ dynarray-skeleton.c. */
+static inline bool
+__dynarray_error (struct dynarray_header *list)
+{
+ return list->allocated == __dynarray_error_marker ();
+}
+
+/* Internal function. Enlarge the dynamically allocated area of the
+ array to make room for one more element. SCRATCH is a pointer to
+ the scratch area (which is not heap-allocated and must not be
+ freed). ELEMENT_SIZE is the size, in bytes, of one element.
+ Return false on failure, true on success. */
+bool __libc_dynarray_emplace_enlarge (struct dynarray_header *,
+ void *scratch, size_t element_size);
+
+/* Internal function. Enlarge the dynamically allocated area of the
+ array to make room for at least SIZE elements (which must be larger
+ than the existing used part of the dynamic array). SCRATCH is a
+ pointer to the scratch area (which is not heap-allocated and must
+ not be freed). ELEMENT_SIZE is the size, in bytes, of one element.
+ Return false on failure, true on success. */
+bool __libc_dynarray_resize (struct dynarray_header *, size_t size,
+ void *scratch, size_t element_size);
+
+/* Internal function. Like __libc_dynarray_resize, but clear the new
+ part of the dynamic array. */
+bool __libc_dynarray_resize_clear (struct dynarray_header *, size_t size,
+ void *scratch, size_t element_size);
+
+/* Internal type. */
+struct dynarray_finalize_result
+{
+ void *array;
+ size_t length;
+};
+
+/* Internal function. Copy the dynamically-allocated area to an
+ explicitly-sized heap allocation. SCRATCH is a pointer to the
+ embedded scratch space. ELEMENT_SIZE is the size, in bytes, of the
+ element type. On success, true is returned, and pointer and length
+ are written to *RESULT. On failure, false is returned. The caller
+ has to take care of some of the memory management; this function is
+ expected to be called from dynarray-skeleton.c. */
+bool __libc_dynarray_finalize (struct dynarray_header *list, void *scratch,
+ size_t element_size,
+ struct dynarray_finalize_result *result);
+
+
+/* Internal function. Terminate the process after an index error.
+ SIZE is the number of elements of the dynamic array. INDEX is the
+ lookup index which triggered the failure. */
+void __libc_dynarray_at_failure (size_t size, size_t index)
+ __attribute__ ((noreturn));
+
+#ifndef _ISOMAC
+libc_hidden_proto (__libc_dynarray_emplace_enlarge)
+libc_hidden_proto (__libc_dynarray_resize)
+libc_hidden_proto (__libc_dynarray_resize_clear)
+libc_hidden_proto (__libc_dynarray_finalize)
+libc_hidden_proto (__libc_dynarray_at_failure)
+#endif
+
+#endif /* _DYNARRAY_H */
diff --git a/malloc/dynarray_at_failure.c b/malloc/dynarray_at_failure.c
new file mode 100644
index 0000000000..4d55647c8b
--- /dev/null
+++ b/malloc/dynarray_at_failure.c
@@ -0,0 +1,31 @@
+/* Report an dynamic array index out of bounds condition.
+ Copyright (C) 2017-2018 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/>. */
+
+#include <dynarray.h>
+#include <stdio.h>
+
+void
+__libc_dynarray_at_failure (size_t size, size_t index)
+{
+ char buf[200];
+ __snprintf (buf, sizeof (buf), "Fatal glibc error: "
+ "array index %zu not less than array length %zu\n",
+ index, size);
+ __libc_fatal (buf);
+}
+libc_hidden_def (__libc_dynarray_at_failure)
diff --git a/malloc/dynarray_emplace_enlarge.c b/malloc/dynarray_emplace_enlarge.c
new file mode 100644
index 0000000000..0408271e27
--- /dev/null
+++ b/malloc/dynarray_emplace_enlarge.c
@@ -0,0 +1,73 @@
+/* Increase the size of a dynamic array in preparation of an emplace operation.
+ Copyright (C) 2017-2018 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/>. */
+
+#include <dynarray.h>
+#include <errno.h>
+#include <malloc-internal.h>
+#include <stdlib.h>
+#include <string.h>
+
+bool
+__libc_dynarray_emplace_enlarge (struct dynarray_header *list,
+ void *scratch, size_t element_size)
+{
+ size_t new_allocated;
+ if (list->allocated == 0)
+ {
+ /* No scratch buffer provided. Choose a reasonable default
+ size. */
+ if (element_size < 4)
+ new_allocated = 16;
+ else if (element_size < 8)
+ new_allocated = 8;
+ else
+ new_allocated = 4;
+ }
+ else
+ /* Increase the allocated size, using an exponential growth
+ policy. */
+ {
+ new_allocated = list->allocated + list->allocated / 2 + 1;
+ if (new_allocated <= list->allocated)
+ {
+ /* Overflow. */
+ __set_errno (ENOMEM);
+ return false;
+ }
+ }
+
+ size_t new_size;
+ if (check_mul_overflow_size_t (new_allocated, element_size, &new_size))
+ return false;
+ void *new_array;
+ if (list->array == scratch)
+ {
+ /* The previous array was not heap-allocated. */
+ new_array = malloc (new_size);
+ if (new_array != NULL && list->array != NULL)
+ memcpy (new_array, list->array, list->used * element_size);
+ }
+ else
+ new_array = realloc (list->array, new_size);
+ if (new_array == NULL)
+ return false;
+ list->array = new_array;
+ list->allocated = new_allocated;
+ return true;
+}
+libc_hidden_def (__libc_dynarray_emplace_enlarge)
diff --git a/malloc/dynarray_finalize.c b/malloc/dynarray_finalize.c
new file mode 100644
index 0000000000..21fa0c71d6
--- /dev/null
+++ b/malloc/dynarray_finalize.c
@@ -0,0 +1,62 @@
+/* Copy the dynamically-allocated area to an explicitly-sized heap allocation.
+ Copyright (C) 2017-2018 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/>. */
+
+#include <dynarray.h>
+#include <stdlib.h>
+#include <string.h>
+
+bool
+__libc_dynarray_finalize (struct dynarray_header *list,
+ void *scratch, size_t element_size,
+ struct dynarray_finalize_result *result)
+{
+ if (__dynarray_error (list))
+ /* The caller will reported the deferred error. */
+ return false;
+
+ size_t used = list->used;
+
+ /* Empty list. */
+ if (used == 0)
+ {
+ /* An empty list could still be backed by a heap-allocated
+ array. Free it if necessary. */
+ if (list->array != scratch)
+ free (list->array);
+ *result = (struct dynarray_finalize_result) { NULL, 0 };
+ return true;
+ }
+
+ size_t allocation_size = used * element_size;
+ void *heap_array = malloc (allocation_size);
+ if (heap_array != NULL)
+ {
+ /* The new array takes ownership of the strings. */
+ if (list->array != NULL)
+ memcpy (heap_array, list->array, allocation_size);
+ if (list->array != scratch)
+ free (list->array);
+ *result = (struct dynarray_finalize_result)
+ { .array = heap_array, .length = used };
+ return true;
+ }
+ else
+ /* The caller will perform the freeing operation. */
+ return false;
+}
+libc_hidden_def (__libc_dynarray_finalize)
diff --git a/malloc/dynarray_resize.c b/malloc/dynarray_resize.c
new file mode 100644
index 0000000000..0bfca1ba4b
--- /dev/null
+++ b/malloc/dynarray_resize.c
@@ -0,0 +1,64 @@
+/* Increase the size of a dynamic array.
+ Copyright (C) 2017-2018 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/>. */
+
+#include <dynarray.h>
+#include <errno.h>
+#include <malloc-internal.h>
+#include <stdlib.h>
+#include <string.h>
+
+bool
+__libc_dynarray_resize (struct dynarray_header *list, size_t size,
+ void *scratch, size_t element_size)
+{
+ /* The existing allocation provides sufficient room. */
+ if (size <= list->allocated)
+ {
+ list->used = size;
+ return true;
+ }
+
+ /* Otherwise, use size as the new allocation size. The caller is
+ expected to provide the final size of the array, so there is no
+ over-allocation here. */
+
+ size_t new_size_bytes;
+ if (check_mul_overflow_size_t (size, element_size, &new_size_bytes))
+ {
+ /* Overflow. */
+ __set_errno (ENOMEM);
+ return false;
+ }
+ void *new_array;
+ if (list->array == scratch)
+ {
+ /* The previous array was not heap-allocated. */
+ new_array = malloc (new_size_bytes);
+ if (new_array != NULL && list->array != NULL)
+ memcpy (new_array, list->array, list->used * element_size);
+ }
+ else
+ new_array = realloc (list->array, new_size_bytes);
+ if (new_array == NULL)
+ return false;
+ list->array = new_array;
+ list->allocated = size;
+ list->used = size;
+ return true;
+}
+libc_hidden_def (__libc_dynarray_resize)
diff --git a/malloc/dynarray_resize_clear.c b/malloc/dynarray_resize_clear.c
new file mode 100644
index 0000000000..e46316f2b4
--- /dev/null
+++ b/malloc/dynarray_resize_clear.c
@@ -0,0 +1,35 @@
+/* Increase the size of a dynamic array and clear the new part.
+ Copyright (C) 2017-2018 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/>. */
+
+#include <dynarray.h>
+#include <stdlib.h>
+#include <string.h>
+
+bool
+__libc_dynarray_resize_clear (struct dynarray_header *list, size_t size,
+ void *scratch, size_t element_size)
+{
+ size_t old_size = list->used;
+ if (!__libc_dynarray_resize (list, size, scratch, element_size))
+ return false;
+ /* __libc_dynarray_resize already checked for overflow. */
+ memset (list->array + (old_size * element_size), 0,
+ (size - old_size) * element_size);
+ return true;
+}
+libc_hidden_def (__libc_dynarray_resize_clear)
diff --git a/malloc/hooks.c b/malloc/hooks.c
index 7a85136b45..ae7305b036 100644
--- a/malloc/hooks.c
+++ b/malloc/hooks.c
@@ -1,5 +1,5 @@
/* Malloc implementation for multiple threads without lock contention.
- Copyright (C) 2001-2016 Free Software Foundation, Inc.
+ Copyright (C) 2001-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Wolfram Gloger <wg@malloc.de>, 2001.
@@ -52,30 +52,10 @@ memalign_hook_ini (size_t alignment, size_t sz, const void *caller)
/* Whether we are using malloc checking. */
static int using_malloc_checking;
-/* A flag that is set by malloc_set_state, to signal that malloc checking
- must not be enabled on the request from the user (via the MALLOC_CHECK_
- environment variable). It is reset by __malloc_check_init to tell
- malloc_set_state that the user has requested malloc checking.
-
- The purpose of this flag is to make sure that malloc checking is not
- enabled when the heap to be restored was constructed without malloc
- checking, and thus does not contain the required magic bytes.
- Otherwise the heap would be corrupted by calls to free and realloc. If
- it turns out that the heap was created with malloc checking and the
- user has requested it malloc_set_state just calls __malloc_check_init
- again to enable it. On the other hand, reusing such a heap without
- further malloc checking is safe. */
-static int disallow_malloc_check;
-
/* Activate a standard set of debugging hooks. */
void
__malloc_check_init (void)
{
- if (disallow_malloc_check)
- {
- disallow_malloc_check = 0;
- return;
- }
using_malloc_checking = 1;
__malloc_hook = malloc_check;
__free_hook = free_check;
@@ -121,12 +101,7 @@ malloc_check_get_size (mchunkptr p)
size -= c)
{
if (c <= 0 || size < (c + 2 * SIZE_SZ))
- {
- malloc_printerr (check_action, "malloc_check_get_size: memory corruption",
- chunk2mem (p),
- chunk_is_mmapped (p) ? NULL : arena_for_chunk (p));
- return 0;
- }
+ malloc_printerr ("malloc_check_get_size: memory corruption");
}
/* chunk2mem size. */
@@ -137,7 +112,6 @@ malloc_check_get_size (mchunkptr p)
into a user pointer with requested size req_sz. */
static void *
-internal_function
mem2mem_check (void *ptr, size_t req_sz)
{
mchunkptr p;
@@ -171,7 +145,6 @@ mem2mem_check (void *ptr, size_t req_sz)
pointer. If the provided pointer is not valid, return NULL. */
static mchunkptr
-internal_function
mem2chunk_check (void *mem, unsigned char **magic_p)
{
mchunkptr p;
@@ -192,7 +165,7 @@ mem2chunk_check (void *mem, unsigned char **magic_p)
((char *) p < mp_.sbrk_base ||
((char *) p + sz) >= (mp_.sbrk_base + main_arena.system_mem))) ||
sz < MINSIZE || sz & MALLOC_ALIGN_MASK || !inuse (p) ||
- (!prev_inuse (p) && (p->prev_size & MALLOC_ALIGN_MASK ||
+ (!prev_inuse (p) && ((prev_size (p) & MALLOC_ALIGN_MASK) != 0 ||
(contig && (char *) prev_chunk (p) < mp_.sbrk_base) ||
next_chunk (prev_chunk (p)) != p)))
return NULL;
@@ -215,9 +188,9 @@ mem2chunk_check (void *mem, unsigned char **magic_p)
offset != 0x20 && offset != 0x40 && offset != 0x80 && offset != 0x100 &&
offset != 0x200 && offset != 0x400 && offset != 0x800 && offset != 0x1000 &&
offset < 0x2000) ||
- !chunk_is_mmapped (p) || (p->size & PREV_INUSE) ||
- ((((unsigned long) p - p->prev_size) & page_mask) != 0) ||
- ((p->prev_size + sz) & page_mask) != 0)
+ !chunk_is_mmapped (p) || prev_inuse (p) ||
+ ((((unsigned long) p - prev_size (p)) & page_mask) != 0) ||
+ ((prev_size (p) + sz) & page_mask) != 0)
return NULL;
for (sz -= 1; (c = ((unsigned char *) p)[sz]) != magic; sz -= c)
@@ -232,17 +205,11 @@ mem2chunk_check (void *mem, unsigned char **magic_p)
return p;
}
-/* Check for corruption of the top chunk, and try to recover if
- necessary. */
-
-static int
-internal_function
+/* Check for corruption of the top chunk. */
+static void
top_check (void)
{
mchunkptr t = top (&main_arena);
- char *brk, *new_brk;
- INTERNAL_SIZE_T front_misalign, sbrk_size;
- unsigned long pagesz = GLRO (dl_pagesize);
if (t == initial_top (&main_arena) ||
(!chunk_is_mmapped (t) &&
@@ -250,34 +217,9 @@ top_check (void)
prev_inuse (t) &&
(!contiguous (&main_arena) ||
(char *) t + chunksize (t) == mp_.sbrk_base + main_arena.system_mem)))
- return 0;
-
- malloc_printerr (check_action, "malloc: top chunk is corrupt", t,
- &main_arena);
-
- /* Try to set up a new top chunk. */
- brk = MORECORE (0);
- front_misalign = (unsigned long) chunk2mem (brk) & MALLOC_ALIGN_MASK;
- if (front_misalign > 0)
- front_misalign = MALLOC_ALIGNMENT - front_misalign;
- sbrk_size = front_misalign + mp_.top_pad + MINSIZE;
- sbrk_size += pagesz - ((unsigned long) (brk + sbrk_size) & (pagesz - 1));
- new_brk = (char *) (MORECORE (sbrk_size));
- if (new_brk == (char *) (MORECORE_FAILURE))
- {
- __set_errno (ENOMEM);
- return -1;
- }
- /* Call the `morecore' hook if necessary. */
- void (*hook) (void) = atomic_forced_read (__after_morecore_hook);
- if (hook)
- (*hook)();
- main_arena.system_mem = (new_brk - mp_.sbrk_base) + sbrk_size;
-
- top (&main_arena) = (mchunkptr) (brk + front_misalign);
- set_head (top (&main_arena), (sbrk_size - front_misalign) | PREV_INUSE);
+ return;
- return 0;
+ malloc_printerr ("malloc: top chunk is corrupt");
}
static void *
@@ -291,9 +233,10 @@ malloc_check (size_t sz, const void *caller)
return NULL;
}
- (void) mutex_lock (&main_arena.mutex);
- victim = (top_check () >= 0) ? _int_malloc (&main_arena, sz + 1) : NULL;
- (void) mutex_unlock (&main_arena.mutex);
+ __libc_lock_lock (main_arena.mutex);
+ top_check ();
+ victim = _int_malloc (&main_arena, sz + 1);
+ __libc_lock_unlock (main_arena.mutex);
return mem2mem_check (victim, sz);
}
@@ -305,24 +248,18 @@ free_check (void *mem, const void *caller)
if (!mem)
return;
- (void) mutex_lock (&main_arena.mutex);
+ __libc_lock_lock (main_arena.mutex);
p = mem2chunk_check (mem, NULL);
if (!p)
- {
- (void) mutex_unlock (&main_arena.mutex);
-
- malloc_printerr (check_action, "free(): invalid pointer", mem,
- &main_arena);
- return;
- }
+ malloc_printerr ("free(): invalid pointer");
if (chunk_is_mmapped (p))
{
- (void) mutex_unlock (&main_arena.mutex);
+ __libc_lock_unlock (main_arena.mutex);
munmap_chunk (p);
return;
}
_int_free (&main_arena, p, 1);
- (void) mutex_unlock (&main_arena.mutex);
+ __libc_lock_unlock (main_arena.mutex);
}
static void *
@@ -345,19 +282,15 @@ realloc_check (void *oldmem, size_t bytes, const void *caller)
free_check (oldmem, NULL);
return NULL;
}
- (void) mutex_lock (&main_arena.mutex);
+ __libc_lock_lock (main_arena.mutex);
const mchunkptr oldp = mem2chunk_check (oldmem, &magic_p);
- (void) mutex_unlock (&main_arena.mutex);
+ __libc_lock_unlock (main_arena.mutex);
if (!oldp)
- {
- malloc_printerr (check_action, "realloc(): invalid pointer", oldmem,
- &main_arena);
- return malloc_check (bytes, NULL);
- }
+ malloc_printerr ("realloc(): invalid pointer");
const INTERNAL_SIZE_T oldsize = chunksize (oldp);
checked_request2size (bytes + 1, nb);
- (void) mutex_lock (&main_arena.mutex);
+ __libc_lock_lock (main_arena.mutex);
if (chunk_is_mmapped (oldp))
{
@@ -374,8 +307,8 @@ realloc_check (void *oldmem, size_t bytes, const void *caller)
else
{
/* Must alloc, copy, free. */
- if (top_check () >= 0)
- newmem = _int_malloc (&main_arena, bytes + 1);
+ top_check ();
+ newmem = _int_malloc (&main_arena, bytes + 1);
if (newmem)
{
memcpy (newmem, oldmem, oldsize - 2 * SIZE_SZ);
@@ -386,21 +319,26 @@ realloc_check (void *oldmem, size_t bytes, const void *caller)
}
else
{
- if (top_check () >= 0)
- {
- INTERNAL_SIZE_T nb;
- checked_request2size (bytes + 1, nb);
- newmem = _int_realloc (&main_arena, oldp, oldsize, nb);
- }
+ top_check ();
+ INTERNAL_SIZE_T nb;
+ checked_request2size (bytes + 1, nb);
+ newmem = _int_realloc (&main_arena, oldp, oldsize, nb);
}
+ DIAG_PUSH_NEEDS_COMMENT;
+#if __GNUC_PREREQ (7, 0)
+ /* GCC 7 warns about magic_p may be used uninitialized. But we never
+ reach here if magic_p is uninitialized. */
+ DIAG_IGNORE_NEEDS_COMMENT (7, "-Wmaybe-uninitialized");
+#endif
/* mem2chunk_check changed the magic byte in the old chunk.
If newmem is NULL, then the old chunk will still be used though,
so we need to invert that change here. */
if (newmem == NULL)
*magic_p ^= 0xFF;
+ DIAG_POP_NEEDS_COMMENT;
- (void) mutex_unlock (&main_arena.mutex);
+ __libc_lock_unlock (main_arena.mutex);
return mem2mem_check (newmem, bytes);
}
@@ -440,32 +378,23 @@ memalign_check (size_t alignment, size_t bytes, const void *caller)
alignment = a;
}
- (void) mutex_lock (&main_arena.mutex);
- mem = (top_check () >= 0) ? _int_memalign (&main_arena, alignment, bytes + 1) :
- NULL;
- (void) mutex_unlock (&main_arena.mutex);
+ __libc_lock_lock (main_arena.mutex);
+ top_check ();
+ mem = _int_memalign (&main_arena, alignment, bytes + 1);
+ __libc_lock_unlock (main_arena.mutex);
return mem2mem_check (mem, bytes);
}
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_25)
-/* Get/set state: malloc_get_state() records the current state of all
- malloc variables (_except_ for the actual heap contents and `hook'
- function pointers) in a system dependent, opaque data structure.
- This data structure is dynamically allocated and can be free()d
- after use. malloc_set_state() restores the state of all malloc
- variables to the previously obtained state. This is especially
- useful when using this malloc as part of a shared library, and when
- the heap contents are saved/restored via some other method. The
- primary example for this is GNU Emacs with its `dumping' procedure.
- `Hook' function pointers are never saved or restored by these
- functions, with two exceptions: If malloc checking was in use when
- malloc_get_state() was called, then malloc_set_state() calls
- __malloc_check_init() if possible; if malloc checking was not in
- use in the recorded state but the user requested malloc checking,
- then the hooks are reset to 0. */
+/* Support for restoring dumped heaps contained in historic Emacs
+ executables. The heap saving feature (malloc_get_state) is no
+ longer implemented in this version of glibc, but we have a heap
+ rewriter in malloc_set_state which transforms the heap into a
+ version compatible with current malloc. */
#define MALLOC_STATE_MAGIC 0x444c4541l
-#define MALLOC_STATE_VERSION (0 * 0x100l + 4l) /* major*0x100 + minor */
+#define MALLOC_STATE_VERSION (0 * 0x100l + 5l) /* major*0x100 + minor */
struct malloc_save_state
{
@@ -480,7 +409,7 @@ struct malloc_save_state
unsigned long mmap_threshold;
int check_action;
unsigned long max_sbrked_mem;
- unsigned long max_total_mem;
+ unsigned long max_total_mem; /* Always 0, for backwards compatibility. */
unsigned int n_mmaps;
unsigned int max_n_mmaps;
unsigned long mmapped_mem;
@@ -492,67 +421,24 @@ struct malloc_save_state
unsigned long narenas;
};
+/* Dummy implementation which always fails. We need to provide this
+ symbol so that existing Emacs binaries continue to work with
+ BIND_NOW. */
void *
-__malloc_get_state (void)
+attribute_compat_text_section
+malloc_get_state (void)
{
- struct malloc_save_state *ms;
- int i;
- mbinptr b;
-
- ms = (struct malloc_save_state *) __libc_malloc (sizeof (*ms));
- if (!ms)
- return 0;
-
- (void) mutex_lock (&main_arena.mutex);
- malloc_consolidate (&main_arena);
- ms->magic = MALLOC_STATE_MAGIC;
- ms->version = MALLOC_STATE_VERSION;
- ms->av[0] = 0;
- ms->av[1] = 0; /* used to be binblocks, now no longer used */
- ms->av[2] = top (&main_arena);
- ms->av[3] = 0; /* used to be undefined */
- for (i = 1; i < NBINS; i++)
- {
- b = bin_at (&main_arena, i);
- if (first (b) == b)
- ms->av[2 * i + 2] = ms->av[2 * i + 3] = 0; /* empty bin */
- else
- {
- ms->av[2 * i + 2] = first (b);
- ms->av[2 * i + 3] = last (b);
- }
- }
- ms->sbrk_base = mp_.sbrk_base;
- ms->sbrked_mem_bytes = main_arena.system_mem;
- ms->trim_threshold = mp_.trim_threshold;
- ms->top_pad = mp_.top_pad;
- ms->n_mmaps_max = mp_.n_mmaps_max;
- ms->mmap_threshold = mp_.mmap_threshold;
- ms->check_action = check_action;
- ms->max_sbrked_mem = main_arena.max_system_mem;
- ms->max_total_mem = 0;
- ms->n_mmaps = mp_.n_mmaps;
- ms->max_n_mmaps = mp_.max_n_mmaps;
- ms->mmapped_mem = mp_.mmapped_mem;
- ms->max_mmapped_mem = mp_.max_mmapped_mem;
- ms->using_malloc_checking = using_malloc_checking;
- ms->max_fast = get_max_fast ();
- ms->arena_test = mp_.arena_test;
- ms->arena_max = mp_.arena_max;
- ms->narenas = narenas;
- (void) mutex_unlock (&main_arena.mutex);
- return (void *) ms;
+ __set_errno (ENOSYS);
+ return NULL;
}
+compat_symbol (libc, malloc_get_state, malloc_get_state, GLIBC_2_0);
int
-__malloc_set_state (void *msptr)
+attribute_compat_text_section
+malloc_set_state (void *msptr)
{
struct malloc_save_state *ms = (struct malloc_save_state *) msptr;
- size_t i;
- mbinptr b;
- disallow_malloc_check = 1;
- ptmalloc_init ();
if (ms->magic != MALLOC_STATE_MAGIC)
return -1;
@@ -560,108 +446,65 @@ __malloc_set_state (void *msptr)
if ((ms->version & ~0xffl) > (MALLOC_STATE_VERSION & ~0xffl))
return -2;
- (void) mutex_lock (&main_arena.mutex);
- /* There are no fastchunks. */
- clear_fastchunks (&main_arena);
- if (ms->version >= 4)
- set_max_fast (ms->max_fast);
- else
- set_max_fast (64); /* 64 used to be the value we always used. */
- for (i = 0; i < NFASTBINS; ++i)
- fastbin (&main_arena, i) = 0;
- for (i = 0; i < BINMAPSIZE; ++i)
- main_arena.binmap[i] = 0;
- top (&main_arena) = ms->av[2];
- main_arena.last_remainder = 0;
- for (i = 1; i < NBINS; i++)
- {
- b = bin_at (&main_arena, i);
- if (ms->av[2 * i + 2] == 0)
- {
- assert (ms->av[2 * i + 3] == 0);
- first (b) = last (b) = b;
- }
+ /* We do not need to perform locking here because malloc_set_state
+ must be called before the first call into the malloc subsytem
+ (usually via __malloc_initialize_hook). pthread_create always
+ calls calloc and thus must be called only afterwards, so there
+ cannot be more than one thread when we reach this point. */
+
+ /* Disable the malloc hooks (and malloc checking). */
+ __malloc_hook = NULL;
+ __realloc_hook = NULL;
+ __free_hook = NULL;
+ __memalign_hook = NULL;
+ using_malloc_checking = 0;
+
+ /* Patch the dumped heap. We no longer try to integrate into the
+ existing heap. Instead, we mark the existing chunks as mmapped.
+ Together with the update to dumped_main_arena_start and
+ dumped_main_arena_end, realloc and free will recognize these
+ chunks as dumped fake mmapped chunks and never free them. */
+
+ /* Find the chunk with the lowest address with the heap. */
+ mchunkptr chunk = NULL;
+ {
+ size_t *candidate = (size_t *) ms->sbrk_base;
+ size_t *end = (size_t *) (ms->sbrk_base + ms->sbrked_mem_bytes);
+ while (candidate < end)
+ if (*candidate != 0)
+ {
+ chunk = mem2chunk ((void *) (candidate + 1));
+ break;
+ }
else
- {
- if (ms->version >= 3 &&
- (i < NSMALLBINS || (largebin_index (chunksize (ms->av[2 * i + 2])) == i &&
- largebin_index (chunksize (ms->av[2 * i + 3])) == i)))
- {
- first (b) = ms->av[2 * i + 2];
- last (b) = ms->av[2 * i + 3];
- /* Make sure the links to the bins within the heap are correct. */
- first (b)->bk = b;
- last (b)->fd = b;
- /* Set bit in binblocks. */
- mark_bin (&main_arena, i);
- }
- else
- {
- /* Oops, index computation from chunksize must have changed.
- Link the whole list into unsorted_chunks. */
- first (b) = last (b) = b;
- b = unsorted_chunks (&main_arena);
- ms->av[2 * i + 2]->bk = b;
- ms->av[2 * i + 3]->fd = b->fd;
- b->fd->bk = ms->av[2 * i + 3];
- b->fd = ms->av[2 * i + 2];
- }
- }
- }
- if (ms->version < 3)
- {
- /* Clear fd_nextsize and bk_nextsize fields. */
- b = unsorted_chunks (&main_arena)->fd;
- while (b != unsorted_chunks (&main_arena))
- {
- if (!in_smallbin_range (chunksize (b)))
- {
- b->fd_nextsize = NULL;
- b->bk_nextsize = NULL;
- }
- b = b->fd;
- }
- }
- mp_.sbrk_base = ms->sbrk_base;
- main_arena.system_mem = ms->sbrked_mem_bytes;
- mp_.trim_threshold = ms->trim_threshold;
- mp_.top_pad = ms->top_pad;
- mp_.n_mmaps_max = ms->n_mmaps_max;
- mp_.mmap_threshold = ms->mmap_threshold;
- check_action = ms->check_action;
- main_arena.max_system_mem = ms->max_sbrked_mem;
- mp_.n_mmaps = ms->n_mmaps;
- mp_.max_n_mmaps = ms->max_n_mmaps;
- mp_.mmapped_mem = ms->mmapped_mem;
- mp_.max_mmapped_mem = ms->max_mmapped_mem;
- /* add version-dependent code here */
- if (ms->version >= 1)
- {
- /* Check whether it is safe to enable malloc checking, or whether
- it is necessary to disable it. */
- if (ms->using_malloc_checking && !using_malloc_checking &&
- !disallow_malloc_check)
- __malloc_check_init ();
- else if (!ms->using_malloc_checking && using_malloc_checking)
- {
- __malloc_hook = NULL;
- __free_hook = NULL;
- __realloc_hook = NULL;
- __memalign_hook = NULL;
- using_malloc_checking = 0;
- }
- }
- if (ms->version >= 4)
+ ++candidate;
+ }
+ if (chunk == NULL)
+ return 0;
+
+ /* Iterate over the dumped heap and patch the chunks so that they
+ are treated as fake mmapped chunks. */
+ mchunkptr top = ms->av[2];
+ while (chunk < top)
{
- mp_.arena_test = ms->arena_test;
- mp_.arena_max = ms->arena_max;
- narenas = ms->narenas;
+ if (inuse (chunk))
+ {
+ /* Mark chunk as mmapped, to trigger the fallback path. */
+ size_t size = chunksize (chunk);
+ set_head (chunk, size | IS_MMAPPED);
+ }
+ chunk = next_chunk (chunk);
}
- check_malloc_state (&main_arena);
- (void) mutex_unlock (&main_arena.mutex);
+ /* The dumped fake mmapped chunks all lie in this address range. */
+ dumped_main_arena_start = (mchunkptr) ms->sbrk_base;
+ dumped_main_arena_end = top;
+
return 0;
}
+compat_symbol (libc, malloc_set_state, malloc_set_state, GLIBC_2_0);
+
+#endif /* SHLIB_COMPAT */
/*
* Local variables:
diff --git a/malloc/malloc-hooks.h b/malloc/malloc-hooks.h
new file mode 100644
index 0000000000..f09d2318f4
--- /dev/null
+++ b/malloc/malloc-hooks.h
@@ -0,0 +1,24 @@
+/* Internal declarations of malloc hooks no longer in the public API.
+ Copyright (C) 2016-2018 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_HOOKS_H
+#define _MALLOC_HOOKS_H
+
+void (*__malloc_initialize_hook) (void);
+
+#endif /* _MALLOC_HOOKS_H */
diff --git a/malloc/malloc-internal.h b/malloc/malloc-internal.h
new file mode 100644
index 0000000000..9cee0fb2d7
--- /dev/null
+++ b/malloc/malloc-internal.h
@@ -0,0 +1,97 @@
+/* Internal declarations for malloc, for use within libc.
+ Copyright (C) 2016-2018 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_INTERNAL_H
+#define _MALLOC_INTERNAL_H
+
+#include <malloc-machine.h>
+#include <malloc-sysdep.h>
+
+/* INTERNAL_SIZE_T is the word-size used for internal bookkeeping of
+ chunk sizes.
+
+ The default version is the same as size_t.
+
+ While not strictly necessary, it is best to define this as an
+ unsigned type, even if size_t is a signed type. This may avoid some
+ artificial size limitations on some systems.
+
+ On a 64-bit machine, you may be able to reduce malloc overhead by
+ defining INTERNAL_SIZE_T to be a 32 bit `unsigned int' at the
+ expense of not being able to handle more than 2^32 of malloced
+ space. If this limitation is acceptable, you are encouraged to set
+ this unless you are on a platform requiring 16byte alignments. In
+ this case the alignment requirements turn out to negate any
+ potential advantages of decreasing size_t word size.
+
+ Implementors: Beware of the possible combinations of:
+ - INTERNAL_SIZE_T might be signed or unsigned, might be 32 or 64 bits,
+ and might be the same width as int or as long
+ - size_t might have different width and signedness as INTERNAL_SIZE_T
+ - int and long might be 32 or 64 bits, and might be the same width
+
+ To deal with this, most comparisons and difference computations
+ among INTERNAL_SIZE_Ts should cast them to unsigned long, being
+ aware of the fact that casting an unsigned int to a wider long does
+ not sign-extend. (This also makes checking for negative numbers
+ awkward.) Some of these casts result in harmless compiler warnings
+ on some systems. */
+#ifndef INTERNAL_SIZE_T
+# define INTERNAL_SIZE_T size_t
+#endif
+
+/* The corresponding word size. */
+#define SIZE_SZ (sizeof (INTERNAL_SIZE_T))
+
+/* The corresponding bit mask value. */
+#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)
+
+
+/* Called in the parent process before a fork. */
+void __malloc_fork_lock_parent (void) attribute_hidden;
+
+/* Called in the parent process after a fork. */
+void __malloc_fork_unlock_parent (void) attribute_hidden;
+
+/* Called in the child process after a fork. */
+void __malloc_fork_unlock_child (void) attribute_hidden;
+
+/* Called as part of the thread shutdown sequence. */
+void __malloc_arena_thread_freeres (void) attribute_hidden;
+
+/* Set *RESULT to LEFT * RIGHT. Return true if the multiplication
+ overflowed. */
+static inline bool
+check_mul_overflow_size_t (size_t left, size_t right, size_t *result)
+{
+#if __GNUC__ >= 5
+ return __builtin_mul_overflow (left, right, result);
+#else
+ /* size_t is unsigned so the behavior on overflow is defined. */
+ *result = left * right;
+ size_t half_size_t = ((size_t) 1) << (8 * sizeof (size_t) / 2);
+ if (__glibc_unlikely ((left | right) >= half_size_t))
+ {
+ if (__glibc_unlikely (right != 0 && *result / right != left))
+ return true;
+ }
+ return false;
+#endif
+}
+
+#endif /* _MALLOC_INTERNAL_H */
diff --git a/malloc/malloc.c b/malloc/malloc.c
index d20d5955db..e247c77b7d 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -1,5 +1,5 @@
/* Malloc implementation for multiple threads without lock contention.
- Copyright (C) 1996-2016 Free Software Foundation, Inc.
+ Copyright (C) 1996-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Wolfram Gloger <wg@malloc.de>
and Doug Lea <dl@cs.oswego.edu>, 2001.
@@ -84,7 +84,6 @@
independent_calloc(size_t n_elements, size_t size, void* chunks[]);
independent_comalloc(size_t n_elements, size_t sizes[], void* chunks[]);
pvalloc(size_t n);
- cfree(void* p);
malloc_trim(size_t pad);
malloc_usable_size(void* p);
malloc_stats();
@@ -173,8 +172,6 @@
Changing default word sizes:
INTERNAL_SIZE_T size_t
- MALLOC_ALIGNMENT MAX (2 * sizeof(INTERNAL_SIZE_T),
- __alignof__ (long double))
Configuration and functionality options:
@@ -216,9 +213,6 @@
#include <stdlib.h> /* for getenv(), abort() */
#include <unistd.h> /* for __libc_enable_secure */
-#include <malloc-machine.h>
-#include <malloc-sysdep.h>
-
#include <atomic.h>
#include <_itoa.h>
#include <bits/wordsize.h>
@@ -229,6 +223,7 @@
#include <unistd.h>
#include <stdio.h> /* needed for malloc_stats */
#include <errno.h>
+#include <assert.h>
#include <shlib-compat.h>
@@ -242,8 +237,15 @@
#include <sys/param.h>
/* For ALIGN_UP et. al. */
-#include <libc-internal.h>
+#include <libc-pointer-arith.h>
+
+/* For DIAG_PUSH/POP_NEEDS_COMMENT et al. */
+#include <libc-diag.h>
+#include <malloc/malloc-internal.h>
+
+/* For SINGLE_THREAD_P. */
+#include <sysdep-cancel.h>
/*
Debugging:
@@ -277,13 +279,9 @@
#define MALLOC_DEBUG 0
#endif
-#ifdef NDEBUG
-# define assert(expr) ((void) 0)
-#else
-# define assert(expr) \
- ((expr) \
- ? ((void) 0) \
- : __malloc_assert (#expr, __FILE__, __LINE__, __func__))
+#ifndef NDEBUG
+# define __assert_fail(assertion, file, line, function) \
+ __malloc_assert(assertion, file, line, function)
extern const char *__progname;
@@ -301,76 +299,30 @@ __malloc_assert (const char *assertion, const char *file, unsigned int line,
}
#endif
+#if USE_TCACHE
+/* We want 64 entries. This is an arbitrary limit, which tunables can reduce. */
+# define TCACHE_MAX_BINS 64
+# define MAX_TCACHE_SIZE tidx2usize (TCACHE_MAX_BINS-1)
-/*
- INTERNAL_SIZE_T is the word-size used for internal bookkeeping
- of chunk sizes.
-
- The default version is the same as size_t.
-
- While not strictly necessary, it is best to define this as an
- unsigned type, even if size_t is a signed type. This may avoid some
- artificial size limitations on some systems.
-
- On a 64-bit machine, you may be able to reduce malloc overhead by
- defining INTERNAL_SIZE_T to be a 32 bit `unsigned int' at the
- expense of not being able to handle more than 2^32 of malloced
- space. If this limitation is acceptable, you are encouraged to set
- this unless you are on a platform requiring 16byte alignments. In
- this case the alignment requirements turn out to negate any
- potential advantages of decreasing size_t word size.
-
- Implementors: Beware of the possible combinations of:
- - INTERNAL_SIZE_T might be signed or unsigned, might be 32 or 64 bits,
- and might be the same width as int or as long
- - size_t might have different width and signedness as INTERNAL_SIZE_T
- - int and long might be 32 or 64 bits, and might be the same width
- To deal with this, most comparisons and difference computations
- among INTERNAL_SIZE_Ts should cast them to unsigned long, being
- aware of the fact that casting an unsigned int to a wider long does
- not sign-extend. (This also makes checking for negative numbers
- awkward.) Some of these casts result in harmless compiler warnings
- on some systems.
-*/
-
-#ifndef INTERNAL_SIZE_T
-#define INTERNAL_SIZE_T size_t
-#endif
-
-/* The corresponding word size */
-#define SIZE_SZ (sizeof(INTERNAL_SIZE_T))
-
-
-/*
- MALLOC_ALIGNMENT is the minimum alignment for malloc'ed chunks.
- It must be a power of two at least 2 * SIZE_SZ, even on machines
- for which smaller alignments would suffice. It may be defined as
- larger than this though. Note however that code and data structures
- are optimized for the case of 8-byte alignment.
-*/
+/* Only used to pre-fill the tunables. */
+# define tidx2usize(idx) (((size_t) idx) * MALLOC_ALIGNMENT + MINSIZE - SIZE_SZ)
+/* When "x" is from chunksize(). */
+# define csize2tidx(x) (((x) - MINSIZE + MALLOC_ALIGNMENT - 1) / MALLOC_ALIGNMENT)
+/* When "x" is a user-provided size. */
+# define usize2tidx(x) csize2tidx (request2size (x))
-#ifndef MALLOC_ALIGNMENT
-# if !SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_16)
-/* This is the correct definition when there is no past ABI to constrain it.
+/* With rounding and alignment, the bins are...
+ idx 0 bytes 0..24 (64-bit) or 0..12 (32-bit)
+ idx 1 bytes 25..40 or 13..20
+ idx 2 bytes 41..56 or 21..28
+ etc. */
- Among configurations with a past ABI constraint, it differs from
- 2*SIZE_SZ only on powerpc32. For the time being, changing this is
- causing more compatibility problems due to malloc_get_state and
- malloc_set_state than will returning blocks not adequately aligned for
- long double objects under -mlong-double-128. */
-
-# define MALLOC_ALIGNMENT (2 *SIZE_SZ < __alignof__ (long double) \
- ? __alignof__ (long double) : 2 *SIZE_SZ)
-# else
-# define MALLOC_ALIGNMENT (2 *SIZE_SZ)
-# endif
+/* This is another arbitrary limit, which tunables can change. Each
+ tcache bin will hold at most this number of chunks. */
+# define TCACHE_FILL_COUNT 7
#endif
-/* The corresponding bit mask value */
-#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)
-
-
/*
REALLOC_ZERO_BYTES_FREES should be set if a call to
@@ -502,6 +454,15 @@ void *(*__morecore)(ptrdiff_t) = __default_morecore;
#define HAVE_MREMAP 0
#endif
+/* We may need to support __malloc_initialize_hook for backwards
+ compatibility. */
+
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_24)
+# define HAVE_MALLOC_INIT_HOOK 1
+#else
+# define HAVE_MALLOC_INIT_HOOK 0
+#endif
+
/*
This version of malloc supports the standard SVID/XPG mallinfo
@@ -580,9 +541,9 @@ void* __libc_calloc(size_t, size_t);
REALLOC_ZERO_BYTES_FREES is set, realloc with a size argument of
zero (re)allocates a minimum-sized chunk.
- Large chunks that were internally obtained via mmap will always
- be reallocated using malloc-copy-free sequences unless
- the system supports MREMAP (currently only linux).
+ Large chunks that were internally obtained via mmap will always be
+ grown using malloc-copy-free sequences unless the system supports
+ MREMAP (currently only linux).
The old unix realloc convention of allowing the last-free'd chunk
to be used as an argument to realloc is not supported.
@@ -649,8 +610,7 @@ libc_hidden_proto (__libc_mallopt)
have been freed but not use resused or consolidated)
hblks: current number of mmapped regions
hblkhd: total bytes held in mmapped regions
- usmblks: the maximum total allocated space. This will be greater
- than current total if trimming has occurred.
+ usmblks: always 0
fsmblks: total bytes held in fastbin blocks
uordblks: current total allocated space (normal or mmapped)
fordblks: total free space
@@ -738,22 +698,6 @@ size_t __malloc_usable_size(void*);
void __malloc_stats(void);
/*
- malloc_get_state(void);
-
- Returns the state of all malloc variables in an opaque data
- structure.
-*/
-void* __malloc_get_state(void);
-
-/*
- malloc_set_state(void* state);
-
- Restore the state of all malloc variables from data obtained with
- malloc_get_state().
-*/
-int __malloc_set_state(void*);
-
-/*
posix_memalign(void **memptr, size_t alignment, size_t size);
POSIX wrapper like memalign(), checking for validity of size.
@@ -1039,13 +983,6 @@ int __posix_memalign(void **, size_t, size_t);
#define RETURN_ADDRESS(X_) (NULL)
#endif
-/* On some platforms we can compile internal, not exported functions better.
- Let the environment provide a macro and define it to be empty if it
- is not available. */
-#ifndef internal_function
-# define internal_function
-#endif
-
/* Forward declarations. */
struct malloc_chunk;
typedef struct malloc_chunk* mchunkptr;
@@ -1059,13 +996,13 @@ static void* _int_realloc(mstate, mchunkptr, INTERNAL_SIZE_T,
static void* _int_memalign(mstate, size_t, size_t);
static void* _mid_memalign(size_t, size_t, void *);
-static void malloc_printerr(int action, const char *str, void *ptr, mstate av);
+static void malloc_printerr(const char *str) __attribute__ ((noreturn));
-static void* internal_function mem2mem_check(void *p, size_t sz);
-static int internal_function top_check(void);
-static void internal_function munmap_chunk(mchunkptr p);
+static void* mem2mem_check(void *p, size_t sz);
+static void top_check(void);
+static void munmap_chunk(mchunkptr p);
#if HAVE_MREMAP
-static mchunkptr internal_function mremap_chunk(mchunkptr p, size_t new_size);
+static mchunkptr mremap_chunk(mchunkptr p, size_t new_size);
#endif
static void* malloc_check(size_t sz, const void *caller);
@@ -1074,10 +1011,6 @@ static void* realloc_check(void* oldmem, size_t bytes,
const void *caller);
static void* memalign_check(size_t alignment, size_t bytes,
const void *caller);
-#ifndef NO_THREADS
-static void* malloc_atfork(size_t sz, const void *caller);
-static void free_atfork(void* mem, const void *caller);
-#endif
/* ------------------ MMAP support ------------------ */
@@ -1110,8 +1043,8 @@ static void free_atfork(void* mem, const void *caller);
struct malloc_chunk {
- INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
- INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */
+ INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free). */
+ INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
@@ -1140,18 +1073,19 @@ struct malloc_chunk {
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Size of previous chunk, if allocated | |
+ | Size of previous chunk, if unallocated (P clear) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Size of chunk, in bytes |M|P|
+ | Size of chunk, in bytes |A|M|P|
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| User data starts here... .
. .
. (malloc_usable_size() bytes) .
. |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Size of chunk |
+ | (size of chunk, but used for application data) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Size of next chunk, in bytes |A|0|1|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
Where "chunk" is the front of the chunk for the purpose of most of
the malloc code, but "mem" is the pointer that is returned to the
@@ -1164,9 +1098,9 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Free chunks are stored in circular doubly-linked lists, and look like this:
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Size of previous chunk |
+ | Size of previous chunk, if unallocated (P clear) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- `head:' | Size of chunk, in bytes |P|
+ `head:' | Size of chunk, in bytes |A|0|P|
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Forward pointer to next chunk in list |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -1178,6 +1112,8 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
`foot:' | Size of chunk, in bytes |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Size of next chunk, in bytes |A|0|0|
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
The P (PREV_INUSE) bit, stored in the unused low-order bit of the
chunk size (which is always a multiple of two words), is an in-use
@@ -1190,12 +1126,21 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
the size of the previous chunk, and might even get a memory
addressing fault when trying to do so.
+ The A (NON_MAIN_ARENA) bit is cleared for chunks on the initial,
+ main arena, described by the main_arena variable. When additional
+ threads are spawned, each thread receives its own arena (up to a
+ configurable limit, after which arenas are reused for multiple
+ threads), and the chunks in these arenas have the A bit set. To
+ find the arena for a chunk on such a non-main arena, heap_for_ptr
+ performs a bit mask operation and indirection through the ar_ptr
+ member of the per-heap header heap_info (see arena.c).
+
Note that the `foot' of the current chunk is actually represented
as the prev_size of the NEXT chunk. This makes it easier to
deal with alignments etc but can be very confusing when trying
to extend or adapt this code.
- The two exceptions to all this are
+ The three exceptions to all this are:
1. The special chunk `top' doesn't bother using the
trailing size field since there is no next contiguous chunk
@@ -1205,8 +1150,16 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2. Chunks allocated via mmap, which have the second-lowest-order
bit M (IS_MMAPPED) set in their size fields. Because they are
- allocated one-by-one, each must contain its own trailing size field.
-
+ allocated one-by-one, each must contain its own trailing size
+ field. If the M bit is set, the other bits are ignored
+ (because mmapped chunks are neither in an arena, nor adjacent
+ to a freed chunk). The M bit is also used for chunks which
+ originally came from a dumped heap via malloc_set_state in
+ hooks.c.
+
+ 3. Chunks in fastbins are treated as allocated chunks from the
+ point of view of the chunk allocator. They are consolidated
+ with their neighbors only in bulk, in malloc_consolidate.
*/
/*
@@ -1252,14 +1205,21 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
MINSIZE : \
((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)
-/* Same, except also perform argument check */
-
-#define checked_request2size(req, sz) \
- if (REQUEST_OUT_OF_RANGE (req)) { \
- __set_errno (ENOMEM); \
- return 0; \
- } \
- (sz) = request2size (req);
+/* Same, except also perform an argument and result check. First, we check
+ that the padding done by request2size didn't result in an integer
+ overflow. Then we check (using REQUEST_OUT_OF_RANGE) that the resulting
+ size isn't so large that a later alignment would lead to another integer
+ overflow. */
+#define checked_request2size(req, sz) \
+({ \
+ (sz) = request2size (req); \
+ if (((sz) < (req)) \
+ || REQUEST_OUT_OF_RANGE (sz)) \
+ { \
+ __set_errno (ENOMEM); \
+ return 0; \
+ } \
+})
/*
--------------- Physical chunk operations ---------------
@@ -1270,14 +1230,14 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#define PREV_INUSE 0x1
/* extract inuse bit of previous chunk */
-#define prev_inuse(p) ((p)->size & PREV_INUSE)
+#define prev_inuse(p) ((p)->mchunk_size & PREV_INUSE)
/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */
#define IS_MMAPPED 0x2
/* check for mmap()'ed chunk */
-#define chunk_is_mmapped(p) ((p)->size & IS_MMAPPED)
+#define chunk_is_mmapped(p) ((p)->mchunk_size & IS_MMAPPED)
/* size field is or'ed with NON_MAIN_ARENA if the chunk was obtained
@@ -1285,8 +1245,11 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
the chunk to the user, if necessary. */
#define NON_MAIN_ARENA 0x4
-/* check for chunk from non-main arena */
-#define chunk_non_main_arena(p) ((p)->size & NON_MAIN_ARENA)
+/* Check for chunk from main arena. */
+#define chunk_main_arena(p) (((p)->mchunk_size & NON_MAIN_ARENA) == 0)
+
+/* Mark a chunk as not being on the main arena. */
+#define set_non_main_arena(p) ((p)->mchunk_size |= NON_MAIN_ARENA)
/*
@@ -1300,50 +1263,61 @@ nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
#define SIZE_BITS (PREV_INUSE | IS_MMAPPED | NON_MAIN_ARENA)
/* Get size, ignoring use bits */
-#define chunksize(p) ((p)->size & ~(SIZE_BITS))
+#define chunksize(p) (chunksize_nomask (p) & ~(SIZE_BITS))
+/* Like chunksize, but do not mask SIZE_BITS. */
+#define chunksize_nomask(p) ((p)->mchunk_size)
/* Ptr to next physical malloc_chunk. */
-#define next_chunk(p) ((mchunkptr) (((char *) (p)) + ((p)->size & ~SIZE_BITS)))
+#define next_chunk(p) ((mchunkptr) (((char *) (p)) + chunksize (p)))
-/* Ptr to previous physical malloc_chunk */
-#define prev_chunk(p) ((mchunkptr) (((char *) (p)) - ((p)->prev_size)))
+/* Size of the chunk below P. Only valid if !prev_inuse (P). */
+#define prev_size(p) ((p)->mchunk_prev_size)
+
+/* Set the size of the chunk below P. Only valid if !prev_inuse (P). */
+#define set_prev_size(p, sz) ((p)->mchunk_prev_size = (sz))
+
+/* Ptr to previous physical malloc_chunk. Only valid if !prev_inuse (P). */
+#define prev_chunk(p) ((mchunkptr) (((char *) (p)) - prev_size (p)))
/* Treat space at ptr + offset as a chunk */
#define chunk_at_offset(p, s) ((mchunkptr) (((char *) (p)) + (s)))
/* extract p's inuse bit */
#define inuse(p) \
- ((((mchunkptr) (((char *) (p)) + ((p)->size & ~SIZE_BITS)))->size) & PREV_INUSE)
+ ((((mchunkptr) (((char *) (p)) + chunksize (p)))->mchunk_size) & PREV_INUSE)
/* set/clear chunk as being inuse without otherwise disturbing */
#define set_inuse(p) \
- ((mchunkptr) (((char *) (p)) + ((p)->size & ~SIZE_BITS)))->size |= PREV_INUSE
+ ((mchunkptr) (((char *) (p)) + chunksize (p)))->mchunk_size |= PREV_INUSE
#define clear_inuse(p) \
- ((mchunkptr) (((char *) (p)) + ((p)->size & ~SIZE_BITS)))->size &= ~(PREV_INUSE)
+ ((mchunkptr) (((char *) (p)) + chunksize (p)))->mchunk_size &= ~(PREV_INUSE)
/* check/set/clear inuse bits in known places */
#define inuse_bit_at_offset(p, s) \
- (((mchunkptr) (((char *) (p)) + (s)))->size & PREV_INUSE)
+ (((mchunkptr) (((char *) (p)) + (s)))->mchunk_size & PREV_INUSE)
#define set_inuse_bit_at_offset(p, s) \
- (((mchunkptr) (((char *) (p)) + (s)))->size |= PREV_INUSE)
+ (((mchunkptr) (((char *) (p)) + (s)))->mchunk_size |= PREV_INUSE)
#define clear_inuse_bit_at_offset(p, s) \
- (((mchunkptr) (((char *) (p)) + (s)))->size &= ~(PREV_INUSE))
+ (((mchunkptr) (((char *) (p)) + (s)))->mchunk_size &= ~(PREV_INUSE))
/* Set size at head, without disturbing its use bit */
-#define set_head_size(p, s) ((p)->size = (((p)->size & SIZE_BITS) | (s)))
+#define set_head_size(p, s) ((p)->mchunk_size = (((p)->mchunk_size & SIZE_BITS) | (s)))
/* Set size/use field */
-#define set_head(p, s) ((p)->size = (s))
+#define set_head(p, s) ((p)->mchunk_size = (s))
/* Set size at footer (only when chunk is not in use) */
-#define set_foot(p, s) (((mchunkptr) ((char *) (p) + (s)))->prev_size = (s))
+#define set_foot(p, s) (((mchunkptr) ((char *) (p) + (s)))->mchunk_prev_size = (s))
+
+#pragma GCC poison mchunk_size
+#pragma GCC poison mchunk_prev_size
/*
-------------------- Internal data structures --------------------
@@ -1412,20 +1386,20 @@ typedef struct malloc_chunk *mbinptr;
/* Take a chunk off a bin list */
#define unlink(AV, P, BK, FD) { \
+ if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) \
+ malloc_printerr ("corrupted size vs. prev_size"); \
FD = P->fd; \
BK = P->bk; \
if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \
- malloc_printerr (check_action, "corrupted double-linked list", P, AV); \
+ malloc_printerr ("corrupted double-linked list"); \
else { \
FD->bk = BK; \
BK->fd = FD; \
- if (!in_smallbin_range (P->size) \
+ if (!in_smallbin_range (chunksize_nomask (P)) \
&& __builtin_expect (P->fd_nextsize != NULL, 0)) { \
if (__builtin_expect (P->fd_nextsize->bk_nextsize != P, 0) \
|| __builtin_expect (P->bk_nextsize->fd_nextsize != P, 0)) \
- malloc_printerr (check_action, \
- "corrupted double-linked list (not small)", \
- P, AV); \
+ malloc_printerr ("corrupted double-linked list (not small)"); \
if (FD->fd_nextsize == NULL) { \
if (P->fd_nextsize == P) \
FD->fd_nextsize = FD->bk_nextsize = FD; \
@@ -1621,27 +1595,6 @@ typedef struct malloc_chunk *mfastbinptr;
#define FASTBIN_CONSOLIDATION_THRESHOLD (65536UL)
/*
- Since the lowest 2 bits in max_fast don't matter in size comparisons,
- they are used as flags.
- */
-
-/*
- FASTCHUNKS_BIT held in max_fast indicates that there are probably
- some fastbin chunks. It is set true on entering a chunk into any
- fastbin, and cleared only in malloc_consolidate.
-
- The truth value is inverted so that have_fastchunks will be true
- upon startup (since statics are zero-filled), simplifying
- initialization checks.
- */
-
-#define FASTCHUNKS_BIT (1U)
-
-#define have_fastchunks(M) (((M)->flags & FASTCHUNKS_BIT) == 0)
-#define clear_fastchunks(M) catomic_or (&(M)->flags, FASTCHUNKS_BIT)
-#define set_fastchunks(M) catomic_and (&(M)->flags, ~FASTCHUNKS_BIT)
-
-/*
NONCONTIGUOUS_BIT indicates that MORECORE does not return contiguous
regions. Otherwise, contiguity is exploited in merging together,
when possible, results from consecutive MORECORE calls.
@@ -1657,40 +1610,63 @@ typedef struct malloc_chunk *mfastbinptr;
#define set_noncontiguous(M) ((M)->flags |= NONCONTIGUOUS_BIT)
#define set_contiguous(M) ((M)->flags &= ~NONCONTIGUOUS_BIT)
-/* ARENA_CORRUPTION_BIT is set if a memory corruption was detected on the
- arena. Such an arena is no longer used to allocate chunks. Chunks
- allocated in that arena before detecting corruption are not freed. */
-
-#define ARENA_CORRUPTION_BIT (4U)
-
-#define arena_is_corrupt(A) (((A)->flags & ARENA_CORRUPTION_BIT))
-#define set_arena_corrupt(A) ((A)->flags |= ARENA_CORRUPTION_BIT)
+/* Maximum size of memory handled in fastbins. */
+static INTERNAL_SIZE_T global_max_fast;
/*
Set value of max_fast.
Use impossibly small value if 0.
- Precondition: there are no existing fastbin chunks.
- Setting the value clears fastchunk bit but preserves noncontiguous bit.
+ Precondition: there are no existing fastbin chunks in the main arena.
+ Since do_check_malloc_state () checks this, we call malloc_consolidate ()
+ before changing max_fast. Note other arenas will leak their fast bin
+ entries if max_fast is reduced.
*/
#define set_max_fast(s) \
global_max_fast = (((s) == 0) \
? SMALLBIN_WIDTH : ((s + SIZE_SZ) & ~MALLOC_ALIGN_MASK))
-#define get_max_fast() global_max_fast
+static inline INTERNAL_SIZE_T
+get_max_fast (void)
+{
+ /* Tell the GCC optimizers that global_max_fast is never larger
+ than MAX_FAST_SIZE. This avoids out-of-bounds array accesses in
+ _int_malloc after constant propagation of the size parameter.
+ (The code never executes because malloc preserves the
+ global_max_fast invariant, but the optimizers may not recognize
+ this.) */
+ if (global_max_fast > MAX_FAST_SIZE)
+ __builtin_unreachable ();
+ return global_max_fast;
+}
/*
----------- Internal state representation and initialization -----------
*/
+/*
+ have_fastchunks indicates that there are probably some fastbin chunks.
+ It is set true on entering a chunk into any fastbin, and cleared early in
+ malloc_consolidate. The value is approximate since it may be set when there
+ are no fastbin chunks, or it may be clear even if there are fastbin chunks
+ available. Given it's sole purpose is to reduce number of redundant calls to
+ malloc_consolidate, it does not affect correctness. As a result we can safely
+ use relaxed atomic accesses.
+ */
+
+
struct malloc_state
{
/* Serialize access. */
- mutex_t mutex;
+ __libc_lock_define (, mutex);
/* Flags (formerly in max_fast). */
int flags;
+ /* Set if the fastbin chunks contain recently inserted free blocks. */
+ /* Note this is a bool but not all targets support atomics on booleans. */
+ int have_fastchunks;
+
/* Fastbins */
mfastbinptr fastbinsY[NFASTBINS];
@@ -1743,13 +1719,21 @@ struct malloc_par
/* Statistics */
INTERNAL_SIZE_T mmapped_mem;
- /*INTERNAL_SIZE_T sbrked_mem;*/
- /*INTERNAL_SIZE_T max_sbrked_mem;*/
INTERNAL_SIZE_T max_mmapped_mem;
- INTERNAL_SIZE_T max_total_mem; /* only kept for NO_THREADS */
/* First address handed out by MORECORE/sbrk. */
char *sbrk_base;
+
+#if USE_TCACHE
+ /* Maximum number of buckets to use. */
+ size_t tcache_bins;
+ size_t tcache_max_bytes;
+ /* Maximum number of chunks in each bucket. */
+ size_t tcache_count;
+ /* Maximum number of chunks to remove from the unsorted list, which
+ aren't used to prefill the cache. */
+ size_t tcache_unsorted_limit;
+#endif
};
/* There are several instances of this struct ("arenas") in this
@@ -1765,6 +1749,19 @@ static struct malloc_state main_arena =
.attached_threads = 1
};
+/* These variables are used for undumping support. Chunked are marked
+ as using mmap, but we leave them alone if they fall into this
+ range. NB: The chunk size for these chunks only includes the
+ initial size field (of SIZE_SZ bytes), there is no trailing size
+ field (unlike with regular mmapped chunks). */
+static mchunkptr dumped_main_arena_start; /* Inclusive. */
+static mchunkptr dumped_main_arena_end; /* Exclusive. */
+
+/* True if the pointer falls into the dumped arena. Use this after
+ chunk_is_mmapped indicates a chunk is mmapped. */
+#define DUMPED_MAIN_ARENA_CHUNK(p) \
+ ((p) >= dumped_main_arena_start && (p) < dumped_main_arena_end)
+
/* There is only one instance of the malloc parameters. */
static struct malloc_par mp_ =
@@ -1775,25 +1772,20 @@ static struct malloc_par mp_ =
.trim_threshold = DEFAULT_TRIM_THRESHOLD,
#define NARENAS_FROM_NCORES(n) ((n) * (sizeof (long) == 4 ? 2 : 8))
.arena_test = NARENAS_FROM_NCORES (1)
+#if USE_TCACHE
+ ,
+ .tcache_count = TCACHE_FILL_COUNT,
+ .tcache_bins = TCACHE_MAX_BINS,
+ .tcache_max_bytes = tidx2usize (TCACHE_MAX_BINS-1),
+ .tcache_unsorted_limit = 0 /* No limit. */
+#endif
};
-
-/* Non public mallopt parameters. */
-#define M_ARENA_TEST -7
-#define M_ARENA_MAX -8
-
-
-/* Maximum size of memory handled in fastbins. */
-static INTERNAL_SIZE_T global_max_fast;
-
/*
Initialize a malloc_state struct.
- This is called only from within malloc_consolidate, which needs
- be called in the same contexts anyway. It is never called directly
- outside of malloc_consolidate because some optimizing compilers try
- to inline it at all call points, which turns out not to be an
- optimization at all. (Inlining it in malloc_consolidate is fine though.)
+ This is called from ptmalloc_init () or from _int_new_arena ()
+ when creating a new arena.
*/
static void
@@ -1815,7 +1807,7 @@ malloc_init_state (mstate av)
set_noncontiguous (av);
if (av == &main_arena)
set_max_fast (DEFAULT_MXFAST);
- av->flags |= FASTCHUNKS_BIT;
+ atomic_store_relaxed (&av->have_fastchunks, false);
av->top = initial_top (av);
}
@@ -1847,7 +1839,12 @@ static void *realloc_hook_ini (void *ptr, size_t sz,
static void *memalign_hook_ini (size_t alignment, size_t sz,
const void *caller) __THROW;
+#if HAVE_MALLOC_INIT_HOOK
void weak_variable (*__malloc_initialize_hook) (void) = NULL;
+compat_symbol (libc, __malloc_initialize_hook,
+ __malloc_initialize_hook, GLIBC_2_0);
+#endif
+
void weak_variable (*__free_hook) (void *__ptr,
const void *) = NULL;
void *weak_variable (*__malloc_hook)
@@ -1860,15 +1857,9 @@ void *weak_variable (*__memalign_hook)
= memalign_hook_ini;
void weak_variable (*__after_morecore_hook) (void) = NULL;
-
-/* ---------------- Error behavior ------------------------------------ */
-
-#ifndef DEFAULT_CHECK_ACTION
-# define DEFAULT_CHECK_ACTION 3
-#endif
-
-static int check_action = DEFAULT_CHECK_ACTION;
-
+/* This function is called from the arena shutdown hook, to free the
+ thread cache (if it exists). */
+static void tcache_thread_shutdown (void);
/* ------------------ Testing support ----------------------------------*/
@@ -1954,7 +1945,7 @@ do_check_chunk (mstate av, mchunkptr p)
assert (prev_inuse (p));
}
}
- else
+ else if (!DUMPED_MAIN_ARENA_CHUNK (p))
{
/* address is outside main heap */
if (contiguous (av) && av->top != initial_top (av))
@@ -1962,7 +1953,7 @@ do_check_chunk (mstate av, mchunkptr p)
assert (((char *) p) < min_address || ((char *) p) >= max_address);
}
/* chunk is page-aligned */
- assert (((p->prev_size + sz) & (GLRO (dl_pagesize) - 1)) == 0);
+ assert (((prev_size (p) + sz) & (GLRO (dl_pagesize) - 1)) == 0);
/* mem is aligned */
assert (aligned_OK (chunk2mem (p)));
}
@@ -1975,7 +1966,7 @@ do_check_chunk (mstate av, mchunkptr p)
static void
do_check_free_chunk (mstate av, mchunkptr p)
{
- INTERNAL_SIZE_T sz = p->size & ~(PREV_INUSE | NON_MAIN_ARENA);
+ INTERNAL_SIZE_T sz = chunksize_nomask (p) & ~(PREV_INUSE | NON_MAIN_ARENA);
mchunkptr next = chunk_at_offset (p, sz);
do_check_chunk (av, p);
@@ -1990,7 +1981,7 @@ do_check_free_chunk (mstate av, mchunkptr p)
assert ((sz & MALLOC_ALIGN_MASK) == 0);
assert (aligned_OK (chunk2mem (p)));
/* ... matching footer field */
- assert (next->prev_size == sz);
+ assert (prev_size (next_chunk (p)) == sz);
/* ... and is fully consolidated */
assert (prev_inuse (p));
assert (next == av->top || inuse (next));
@@ -2050,15 +2041,15 @@ do_check_inuse_chunk (mstate av, mchunkptr p)
static void
do_check_remalloced_chunk (mstate av, mchunkptr p, INTERNAL_SIZE_T s)
{
- INTERNAL_SIZE_T sz = p->size & ~(PREV_INUSE | NON_MAIN_ARENA);
+ INTERNAL_SIZE_T sz = chunksize_nomask (p) & ~(PREV_INUSE | NON_MAIN_ARENA);
if (!chunk_is_mmapped (p))
{
assert (av == arena_for_chunk (p));
- if (chunk_non_main_arena (p))
- assert (av != &main_arena);
- else
+ if (chunk_main_arena (p))
assert (av == &main_arena);
+ else
+ assert (av != &main_arena);
}
do_check_inuse_chunk (av, p);
@@ -2126,8 +2117,11 @@ do_check_malloc_state (mstate av)
/* alignment is a power of 2 */
assert ((MALLOC_ALIGNMENT & (MALLOC_ALIGNMENT - 1)) == 0);
- /* cannot run remaining checks until fully initialized */
- if (av->top == 0 || av->top == initial_top (av))
+ /* Check the arena is initialized. */
+ assert (av->top != 0);
+
+ /* No memory has been allocated yet, so doing more tests is not possible. */
+ if (av->top == initial_top (av))
return;
/* pagesize is a power of 2 */
@@ -2175,11 +2169,6 @@ do_check_malloc_state (mstate av)
}
}
- if (total != 0)
- assert (have_fastchunks (av));
- else if (!have_fastchunks (av))
- assert (total == 0);
-
/* check normal bins */
for (i = 1; i < NBINS; ++i)
{
@@ -2347,12 +2336,13 @@ sysmalloc (INTERNAL_SIZE_T nb, mstate av)
{
correction = MALLOC_ALIGNMENT - front_misalign;
p = (mchunkptr) (mm + correction);
- p->prev_size = correction;
+ set_prev_size (p, correction);
set_head (p, (size - correction) | IS_MMAPPED);
}
else
{
p = (mchunkptr) mm;
+ set_prev_size (p, 0);
set_head (p, size | IS_MMAPPED);
}
@@ -2410,7 +2400,6 @@ sysmalloc (INTERNAL_SIZE_T nb, mstate av)
&& grow_heap (old_heap, MINSIZE + nb - old_size) == 0)
{
av->system_mem += old_heap->size - old_heap_size;
- arena_mem += old_heap->size - old_heap_size;
set_head (old_top, (((char *) old_heap + old_heap->size) - (char *) old_top)
| PREV_INUSE);
}
@@ -2420,7 +2409,6 @@ sysmalloc (INTERNAL_SIZE_T nb, mstate av)
heap->ar_ptr = av;
heap->prev = old_heap;
av->system_mem += heap->size;
- arena_mem += heap->size;
/* Set up the new top. */
top (av) = chunk_at_offset (heap, sizeof (*heap));
set_head (top (av), (heap->size - sizeof (*heap)) | PREV_INUSE);
@@ -2548,11 +2536,8 @@ sysmalloc (INTERNAL_SIZE_T nb, mstate av)
set_head (old_top, (size + old_size) | PREV_INUSE);
else if (contiguous (av) && old_size && brk < old_end)
- {
- /* Oops! Someone else killed our space.. Can't touch anything. */
- malloc_printerr (3, "break adjusted to free malloc space", brk,
- av);
- }
+ /* Oops! Someone else killed our space.. Can't touch anything. */
+ malloc_printerr ("break adjusted to free malloc space");
/*
Otherwise, make adjustments:
@@ -2704,11 +2689,10 @@ sysmalloc (INTERNAL_SIZE_T nb, mstate av)
intentional. We need the fencepost, even if old_top otherwise gets
lost.
*/
- chunk_at_offset (old_top, old_size)->size =
- (2 * SIZE_SZ) | PREV_INUSE;
-
- chunk_at_offset (old_top, old_size + 2 * SIZE_SZ)->size =
- (2 * SIZE_SZ) | PREV_INUSE;
+ set_head (chunk_at_offset (old_top, old_size),
+ (2 * SIZE_SZ) | PREV_INUSE);
+ set_head (chunk_at_offset (old_top, old_size + 2 * SIZE_SZ),
+ (2 * SIZE_SZ) | PREV_INUSE);
/* If possible, release the rest. */
if (old_size >= MINSIZE)
@@ -2824,26 +2808,26 @@ systrim (size_t pad, mstate av)
}
static void
-internal_function
munmap_chunk (mchunkptr p)
{
INTERNAL_SIZE_T size = chunksize (p);
assert (chunk_is_mmapped (p));
- uintptr_t block = (uintptr_t) p - p->prev_size;
- size_t total_size = p->prev_size + size;
+ /* Do nothing if the chunk is a faked mmapped chunk in the dumped
+ main arena. We never free this memory. */
+ if (DUMPED_MAIN_ARENA_CHUNK (p))
+ return;
+
+ uintptr_t block = (uintptr_t) p - prev_size (p);
+ size_t total_size = prev_size (p) + size;
/* Unfortunately we have to do the compilers job by hand here. Normally
we would test BLOCK and TOTAL-SIZE separately for compliance with the
page size. But gcc does not recognize the optimization possibility
(in the moment at least) so we combine the two values into one before
the bit test. */
if (__builtin_expect (((block | total_size) & (GLRO (dl_pagesize) - 1)) != 0, 0))
- {
- malloc_printerr (check_action, "munmap_chunk(): invalid pointer",
- chunk2mem (p), NULL);
- return;
- }
+ malloc_printerr ("munmap_chunk(): invalid pointer");
atomic_decrement (&mp_.n_mmaps);
atomic_add (&mp_.mmapped_mem, -total_size);
@@ -2857,11 +2841,10 @@ munmap_chunk (mchunkptr p)
#if HAVE_MREMAP
static mchunkptr
-internal_function
mremap_chunk (mchunkptr p, size_t new_size)
{
size_t pagesize = GLRO (dl_pagesize);
- INTERNAL_SIZE_T offset = p->prev_size;
+ INTERNAL_SIZE_T offset = prev_size (p);
INTERNAL_SIZE_T size = chunksize (p);
char *cp;
@@ -2885,7 +2868,7 @@ mremap_chunk (mchunkptr p, size_t new_size)
assert (aligned_OK (chunk2mem (p)));
- assert ((p->prev_size == offset));
+ assert (prev_size (p) == offset);
set_head (p, (new_size - offset) | IS_MMAPPED);
INTERNAL_SIZE_T new;
@@ -2898,6 +2881,132 @@ mremap_chunk (mchunkptr p, size_t new_size)
/*------------------------ Public wrappers. --------------------------------*/
+#if USE_TCACHE
+
+/* We overlay this structure on the user-data portion of a chunk when
+ the chunk is stored in the per-thread cache. */
+typedef struct tcache_entry
+{
+ struct tcache_entry *next;
+} tcache_entry;
+
+/* There is one of these for each thread, which contains the
+ per-thread cache (hence "tcache_perthread_struct"). Keeping
+ overall size low is mildly important. Note that COUNTS and ENTRIES
+ are redundant (we could have just counted the linked list each
+ time), this is for performance reasons. */
+typedef struct tcache_perthread_struct
+{
+ char counts[TCACHE_MAX_BINS];
+ tcache_entry *entries[TCACHE_MAX_BINS];
+} tcache_perthread_struct;
+
+static __thread bool tcache_shutting_down = false;
+static __thread tcache_perthread_struct *tcache = NULL;
+
+/* Caller must ensure that we know tc_idx is valid and there's room
+ for more chunks. */
+static __always_inline void
+tcache_put (mchunkptr chunk, size_t tc_idx)
+{
+ tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
+ assert (tc_idx < TCACHE_MAX_BINS);
+ e->next = tcache->entries[tc_idx];
+ tcache->entries[tc_idx] = e;
+ ++(tcache->counts[tc_idx]);
+}
+
+/* Caller must ensure that we know tc_idx is valid and there's
+ available chunks to remove. */
+static __always_inline void *
+tcache_get (size_t tc_idx)
+{
+ tcache_entry *e = tcache->entries[tc_idx];
+ assert (tc_idx < TCACHE_MAX_BINS);
+ assert (tcache->entries[tc_idx] > 0);
+ tcache->entries[tc_idx] = e->next;
+ --(tcache->counts[tc_idx]);
+ return (void *) e;
+}
+
+static void
+tcache_thread_shutdown (void)
+{
+ int i;
+ tcache_perthread_struct *tcache_tmp = tcache;
+
+ if (!tcache)
+ return;
+
+ /* Disable the tcache and prevent it from being reinitialized. */
+ tcache = NULL;
+ tcache_shutting_down = true;
+
+ /* Free all of the entries and the tcache itself back to the arena
+ heap for coalescing. */
+ for (i = 0; i < TCACHE_MAX_BINS; ++i)
+ {
+ while (tcache_tmp->entries[i])
+ {
+ tcache_entry *e = tcache_tmp->entries[i];
+ tcache_tmp->entries[i] = e->next;
+ __libc_free (e);
+ }
+ }
+
+ __libc_free (tcache_tmp);
+}
+
+static void
+tcache_init(void)
+{
+ mstate ar_ptr;
+ void *victim = 0;
+ const size_t bytes = sizeof (tcache_perthread_struct);
+
+ if (tcache_shutting_down)
+ return;
+
+ arena_get (ar_ptr, bytes);
+ victim = _int_malloc (ar_ptr, bytes);
+ if (!victim && ar_ptr != NULL)
+ {
+ ar_ptr = arena_get_retry (ar_ptr, bytes);
+ victim = _int_malloc (ar_ptr, bytes);
+ }
+
+
+ if (ar_ptr != NULL)
+ __libc_lock_unlock (ar_ptr->mutex);
+
+ /* In a low memory situation, we may not be able to allocate memory
+ - in which case, we just keep trying later. However, we
+ typically do this very early, so either there is sufficient
+ memory, or there isn't enough memory to do non-trivial
+ allocations anyway. */
+ if (victim)
+ {
+ tcache = (tcache_perthread_struct *) victim;
+ memset (tcache, 0, sizeof (tcache_perthread_struct));
+ }
+
+}
+
+# define MAYBE_INIT_TCACHE() \
+ if (__glibc_unlikely (tcache == NULL)) \
+ tcache_init();
+
+#else /* !USE_TCACHE */
+# define MAYBE_INIT_TCACHE()
+
+static void
+tcache_thread_shutdown (void)
+{
+ /* Nothing to do if there is no thread cache. */
+}
+
+#endif /* !USE_TCACHE */
+
void *
__libc_malloc (size_t bytes)
{
@@ -2908,6 +3017,32 @@ __libc_malloc (size_t bytes)
= atomic_forced_read (__malloc_hook);
if (__builtin_expect (hook != NULL, 0))
return (*hook)(bytes, RETURN_ADDRESS (0));
+#if USE_TCACHE
+ /* int_free also calls request2size, be careful to not pad twice. */
+ size_t tbytes;
+ checked_request2size (bytes, tbytes);
+ size_t tc_idx = csize2tidx (tbytes);
+
+ MAYBE_INIT_TCACHE ();
+
+ DIAG_PUSH_NEEDS_COMMENT;
+ if (tc_idx < mp_.tcache_bins
+ /*&& tc_idx < TCACHE_MAX_BINS*/ /* to appease gcc */
+ && tcache
+ && tcache->entries[tc_idx] != NULL)
+ {
+ return tcache_get (tc_idx);
+ }
+ DIAG_POP_NEEDS_COMMENT;
+#endif
+
+ if (SINGLE_THREAD_P)
+ {
+ victim = _int_malloc (&main_arena, bytes);
+ assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||
+ &main_arena == arena_for_chunk (mem2chunk (victim)));
+ return victim;
+ }
arena_get (ar_ptr, bytes);
@@ -2922,7 +3057,7 @@ __libc_malloc (size_t bytes)
}
if (ar_ptr != NULL)
- (void) mutex_unlock (&ar_ptr->mutex);
+ __libc_lock_unlock (ar_ptr->mutex);
assert (!victim || chunk_is_mmapped (mem2chunk (victim)) ||
ar_ptr == arena_for_chunk (mem2chunk (victim)));
@@ -2951,10 +3086,12 @@ __libc_free (void *mem)
if (chunk_is_mmapped (p)) /* release mmapped memory. */
{
- /* see if the dynamic brk/mmap threshold needs adjusting */
+ /* See if the dynamic brk/mmap threshold needs adjusting.
+ Dumped fake mmapped chunks do not affect the threshold. */
if (!mp_.no_dyn_threshold
- && p->size > mp_.mmap_threshold
- && p->size <= DEFAULT_MMAP_THRESHOLD_MAX)
+ && chunksize_nomask (p) > mp_.mmap_threshold
+ && chunksize_nomask (p) <= DEFAULT_MMAP_THRESHOLD_MAX
+ && !DUMPED_MAIN_ARENA_CHUNK (p))
{
mp_.mmap_threshold = chunksize (p);
mp_.trim_threshold = 2 * mp_.mmap_threshold;
@@ -2965,6 +3102,8 @@ __libc_free (void *mem)
return;
}
+ MAYBE_INIT_TCACHE ();
+
ar_ptr = arena_for_chunk (p);
_int_free (ar_ptr, p, 0);
}
@@ -3002,24 +3141,44 @@ __libc_realloc (void *oldmem, size_t bytes)
if (chunk_is_mmapped (oldp))
ar_ptr = NULL;
else
- ar_ptr = arena_for_chunk (oldp);
-
- /* Little security check which won't hurt performance: the
- allocator never wrapps around at the end of the address space.
- Therefore we can exclude some size values which might appear
- here by accident or by "design" from some intruder. */
- if (__builtin_expect ((uintptr_t) oldp > (uintptr_t) -oldsize, 0)
- || __builtin_expect (misaligned_chunk (oldp), 0))
{
- malloc_printerr (check_action, "realloc(): invalid pointer", oldmem,
- ar_ptr);
- return NULL;
+ MAYBE_INIT_TCACHE ();
+ ar_ptr = arena_for_chunk (oldp);
}
+ /* Little security check which won't hurt performance: the allocator
+ never wrapps around at the end of the address space. Therefore
+ we can exclude some size values which might appear here by
+ accident or by "design" from some intruder. We need to bypass
+ this check for dumped fake mmap chunks from the old main arena
+ because the new malloc may provide additional alignment. */
+ if ((__builtin_expect ((uintptr_t) oldp > (uintptr_t) -oldsize, 0)
+ || __builtin_expect (misaligned_chunk (oldp), 0))
+ && !DUMPED_MAIN_ARENA_CHUNK (oldp))
+ malloc_printerr ("realloc(): invalid pointer");
+
checked_request2size (bytes, nb);
if (chunk_is_mmapped (oldp))
{
+ /* If this is a faked mmapped chunk from the dumped main arena,
+ always make a copy (and do not free the old chunk). */
+ if (DUMPED_MAIN_ARENA_CHUNK (oldp))
+ {
+ /* Must alloc, copy, free. */
+ void *newmem = __libc_malloc (bytes);
+ if (newmem == 0)
+ return NULL;
+ /* Copy as many bytes as are available from the old chunk
+ and fit into the new size. NB: The overhead for faked
+ mmapped chunks is only SIZE_SZ, not 2 * SIZE_SZ as for
+ regular mmapped chunks. */
+ if (bytes > oldsize - SIZE_SZ)
+ bytes = oldsize - SIZE_SZ;
+ memcpy (newmem, oldmem, bytes);
+ return newmem;
+ }
+
void *newmem;
#if HAVE_MREMAP
@@ -3041,11 +3200,20 @@ __libc_realloc (void *oldmem, size_t bytes)
return newmem;
}
- (void) mutex_lock (&ar_ptr->mutex);
+ if (SINGLE_THREAD_P)
+ {
+ newp = _int_realloc (ar_ptr, oldp, oldsize, nb);
+ assert (!newp || chunk_is_mmapped (mem2chunk (newp)) ||
+ ar_ptr == arena_for_chunk (mem2chunk (newp)));
+
+ return newp;
+ }
+
+ __libc_lock_lock (ar_ptr->mutex);
newp = _int_realloc (ar_ptr, oldp, oldsize, nb);
- (void) mutex_unlock (&ar_ptr->mutex);
+ __libc_lock_unlock (ar_ptr->mutex);
assert (!newp || chunk_is_mmapped (mem2chunk (newp)) ||
ar_ptr == arena_for_chunk (mem2chunk (newp)));
@@ -3116,6 +3284,15 @@ _mid_memalign (size_t alignment, size_t bytes, void *address)
alignment = a;
}
+ if (SINGLE_THREAD_P)
+ {
+ p = _int_memalign (&main_arena, alignment, bytes);
+ assert (!p || chunk_is_mmapped (mem2chunk (p)) ||
+ &main_arena == arena_for_chunk (mem2chunk (p)));
+
+ return p;
+ }
+
arena_get (ar_ptr, bytes + alignment + MINSIZE);
p = _int_memalign (ar_ptr, alignment, bytes);
@@ -3127,7 +3304,7 @@ _mid_memalign (size_t alignment, size_t bytes, void *address)
}
if (ar_ptr != NULL)
- (void) mutex_unlock (&ar_ptr->mutex);
+ __libc_lock_unlock (ar_ptr->mutex);
assert (!p || chunk_is_mmapped (mem2chunk (p)) ||
ar_ptr == arena_for_chunk (mem2chunk (p)));
@@ -3206,7 +3383,13 @@ __libc_calloc (size_t n, size_t elem_size)
sz = bytes;
- arena_get (av, sz);
+ MAYBE_INIT_TCACHE ();
+
+ if (SINGLE_THREAD_P)
+ av = &main_arena;
+ else
+ arena_get (av, sz);
+
if (av)
{
/* Check if we hand out the top chunk, in which case there may be no
@@ -3236,19 +3419,21 @@ __libc_calloc (size_t n, size_t elem_size)
}
mem = _int_malloc (av, sz);
-
assert (!mem || chunk_is_mmapped (mem2chunk (mem)) ||
av == arena_for_chunk (mem2chunk (mem)));
- if (mem == 0 && av != NULL)
+ if (!SINGLE_THREAD_P)
{
- LIBC_PROBE (memory_calloc_retry, 1, sz);
- av = arena_get_retry (av, sz);
- mem = _int_malloc (av, sz);
- }
+ if (mem == 0 && av != NULL)
+ {
+ LIBC_PROBE (memory_calloc_retry, 1, sz);
+ av = arena_get_retry (av, sz);
+ mem = _int_malloc (av, sz);
+ }
- if (av != NULL)
- (void) mutex_unlock (&av->mutex);
+ if (av != NULL)
+ __libc_lock_unlock (av->mutex);
+ }
/* Allocation failed even after a retry. */
if (mem == 0)
@@ -3336,7 +3521,9 @@ _int_malloc (mstate av, size_t bytes)
mchunkptr fwd; /* misc temp for linking */
mchunkptr bck; /* misc temp for linking */
- const char *errstr = NULL;
+#if USE_TCACHE
+ size_t tcache_unsorted_count; /* count of unsorted chunks processed */
+#endif
/*
Convert request size to internal form by adding SIZE_SZ bytes
@@ -3365,33 +3552,64 @@ _int_malloc (mstate av, size_t bytes)
can try it without checking, which saves some time on this fast path.
*/
+#define REMOVE_FB(fb, victim, pp) \
+ do \
+ { \
+ victim = pp; \
+ if (victim == NULL) \
+ break; \
+ } \
+ while ((pp = catomic_compare_and_exchange_val_acq (fb, victim->fd, victim)) \
+ != victim); \
+
if ((unsigned long) (nb) <= (unsigned long) (get_max_fast ()))
{
idx = fastbin_index (nb);
mfastbinptr *fb = &fastbin (av, idx);
- mchunkptr pp = *fb;
- do
- {
- victim = pp;
- if (victim == NULL)
- break;
- }
- while ((pp = catomic_compare_and_exchange_val_acq (fb, victim->fd, victim))
- != victim);
- if (victim != 0)
- {
- if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))
- {
- errstr = "malloc(): memory corruption (fast)";
- errout:
- malloc_printerr (check_action, errstr, chunk2mem (victim), av);
- return NULL;
- }
- check_remalloced_chunk (av, victim, nb);
- void *p = chunk2mem (victim);
- alloc_perturb (p, bytes);
- return p;
- }
+ mchunkptr pp;
+ victim = *fb;
+
+ if (victim != NULL)
+ {
+ if (SINGLE_THREAD_P)
+ *fb = victim->fd;
+ else
+ REMOVE_FB (fb, pp, victim);
+ if (__glibc_likely (victim != NULL))
+ {
+ size_t victim_idx = fastbin_index (chunksize (victim));
+ if (__builtin_expect (victim_idx != idx, 0))
+ malloc_printerr ("malloc(): memory corruption (fast)");
+ check_remalloced_chunk (av, victim, nb);
+#if USE_TCACHE
+ /* While we're here, if we see other chunks of the same size,
+ stash them in the tcache. */
+ size_t tc_idx = csize2tidx (nb);
+ if (tcache && tc_idx < mp_.tcache_bins)
+ {
+ mchunkptr tc_victim;
+
+ /* While bin not empty and tcache not full, copy chunks. */
+ while (tcache->counts[tc_idx] < mp_.tcache_count
+ && (tc_victim = *fb) != NULL)
+ {
+ if (SINGLE_THREAD_P)
+ *fb = tc_victim->fd;
+ else
+ {
+ REMOVE_FB (fb, pp, tc_victim);
+ if (__glibc_unlikely (tc_victim == NULL))
+ break;
+ }
+ tcache_put (tc_victim, tc_idx);
+ }
+ }
+#endif
+ void *p = chunk2mem (victim);
+ alloc_perturb (p, bytes);
+ return p;
+ }
+ }
}
/*
@@ -3409,27 +3627,45 @@ _int_malloc (mstate av, size_t bytes)
if ((victim = last (bin)) != bin)
{
- if (victim == 0) /* initialization check */
- malloc_consolidate (av);
- else
- {
- bck = victim->bk;
- if (__glibc_unlikely (bck->fd != victim))
- {
- errstr = "malloc(): smallbin double linked list corrupted";
- goto errout;
- }
- set_inuse_bit_at_offset (victim, nb);
- bin->bk = bck;
- bck->fd = bin;
+ bck = victim->bk;
+ if (__glibc_unlikely (bck->fd != victim))
+ malloc_printerr ("malloc(): smallbin double linked list corrupted");
+ set_inuse_bit_at_offset (victim, nb);
+ bin->bk = bck;
+ bck->fd = bin;
+
+ if (av != &main_arena)
+ set_non_main_arena (victim);
+ check_malloced_chunk (av, victim, nb);
+#if USE_TCACHE
+ /* While we're here, if we see other chunks of the same size,
+ stash them in the tcache. */
+ size_t tc_idx = csize2tidx (nb);
+ if (tcache && tc_idx < mp_.tcache_bins)
+ {
+ mchunkptr tc_victim;
- if (av != &main_arena)
- victim->size |= NON_MAIN_ARENA;
- check_malloced_chunk (av, victim, nb);
- void *p = chunk2mem (victim);
- alloc_perturb (p, bytes);
- return p;
- }
+ /* While bin not empty and tcache not full, copy chunks over. */
+ while (tcache->counts[tc_idx] < mp_.tcache_count
+ && (tc_victim = last (bin)) != bin)
+ {
+ if (tc_victim != 0)
+ {
+ bck = tc_victim->bk;
+ set_inuse_bit_at_offset (tc_victim, nb);
+ if (av != &main_arena)
+ set_non_main_arena (tc_victim);
+ bin->bk = bck;
+ bck->fd = bin;
+
+ tcache_put (tc_victim, tc_idx);
+ }
+ }
+ }
+#endif
+ void *p = chunk2mem (victim);
+ alloc_perturb (p, bytes);
+ return p;
}
}
@@ -3447,7 +3683,7 @@ _int_malloc (mstate av, size_t bytes)
else
{
idx = largebin_index (nb);
- if (have_fastchunks (av))
+ if (atomic_load_relaxed (&av->have_fastchunks))
malloc_consolidate (av);
}
@@ -3464,16 +3700,26 @@ _int_malloc (mstate av, size_t bytes)
otherwise need to expand memory to service a "small" request.
*/
+#if USE_TCACHE
+ INTERNAL_SIZE_T tcache_nb = 0;
+ size_t tc_idx = csize2tidx (nb);
+ if (tcache && tc_idx < mp_.tcache_bins)
+ tcache_nb = nb;
+ int return_cached = 0;
+
+ tcache_unsorted_count = 0;
+#endif
+
for (;; )
{
int iters = 0;
while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
{
bck = victim->bk;
- if (__builtin_expect (victim->size <= 2 * SIZE_SZ, 0)
- || __builtin_expect (victim->size > av->system_mem, 0))
- malloc_printerr (check_action, "malloc(): memory corruption",
- chunk2mem (victim), av);
+ if (__builtin_expect (chunksize_nomask (victim) <= 2 * SIZE_SZ, 0)
+ || __builtin_expect (chunksize_nomask (victim)
+ > av->system_mem, 0))
+ malloc_printerr ("malloc(): memory corruption");
size = chunksize (victim);
/*
@@ -3513,6 +3759,8 @@ _int_malloc (mstate av, size_t bytes)
}
/* remove from unsorted list */
+ if (__glibc_unlikely (bck->fd != victim))
+ malloc_printerr ("malloc(): corrupted unsorted chunks 3");
unsorted_chunks (av)->bk = bck;
bck->fd = unsorted_chunks (av);
@@ -3522,11 +3770,27 @@ _int_malloc (mstate av, size_t bytes)
{
set_inuse_bit_at_offset (victim, size);
if (av != &main_arena)
- victim->size |= NON_MAIN_ARENA;
+ set_non_main_arena (victim);
+#if USE_TCACHE
+ /* Fill cache first, return to user only if cache fills.
+ We may return one of these chunks later. */
+ if (tcache_nb
+ && tcache->counts[tc_idx] < mp_.tcache_count)
+ {
+ tcache_put (victim, tc_idx);
+ return_cached = 1;
+ continue;
+ }
+ else
+ {
+#endif
check_malloced_chunk (av, victim, nb);
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
+#if USE_TCACHE
+ }
+#endif
}
/* place chunk in bin */
@@ -3549,8 +3813,9 @@ _int_malloc (mstate av, size_t bytes)
/* Or with inuse bit to speed comparisons */
size |= PREV_INUSE;
/* if smaller than smallest, bypass loop below */
- assert ((bck->bk->size & NON_MAIN_ARENA) == 0);
- if ((unsigned long) (size) < (unsigned long) (bck->bk->size))
+ assert (chunk_main_arena (bck->bk));
+ if ((unsigned long) (size)
+ < (unsigned long) chunksize_nomask (bck->bk))
{
fwd = bck;
bck = bck->bk;
@@ -3561,14 +3826,15 @@ _int_malloc (mstate av, size_t bytes)
}
else
{
- assert ((fwd->size & NON_MAIN_ARENA) == 0);
- while ((unsigned long) size < fwd->size)
+ assert (chunk_main_arena (fwd));
+ while ((unsigned long) size < chunksize_nomask (fwd))
{
fwd = fwd->fd_nextsize;
- assert ((fwd->size & NON_MAIN_ARENA) == 0);
+ assert (chunk_main_arena (fwd));
}
- if ((unsigned long) size == (unsigned long) fwd->size)
+ if ((unsigned long) size
+ == (unsigned long) chunksize_nomask (fwd))
/* Always insert in the second position. */
fwd = fwd->fd;
else
@@ -3591,11 +3857,31 @@ _int_malloc (mstate av, size_t bytes)
fwd->bk = victim;
bck->fd = victim;
+#if USE_TCACHE
+ /* If we've processed as many chunks as we're allowed while
+ filling the cache, return one of the cached ones. */
+ ++tcache_unsorted_count;
+ if (return_cached
+ && mp_.tcache_unsorted_limit > 0
+ && tcache_unsorted_count > mp_.tcache_unsorted_limit)
+ {
+ return tcache_get (tc_idx);
+ }
+#endif
+
#define MAX_ITERS 10000
if (++iters >= MAX_ITERS)
break;
}
+#if USE_TCACHE
+ /* If all the small chunks we found ended up cached, return one now. */
+ if (return_cached)
+ {
+ return tcache_get (tc_idx);
+ }
+#endif
+
/*
If a large request, scan through the chunks of current bin in
sorted order to find smallest that fits. Use the skip list for this.
@@ -3606,8 +3892,9 @@ _int_malloc (mstate av, size_t bytes)
bin = bin_at (av, idx);
/* skip scan if empty or largest chunk is too small */
- if ((victim = first (bin)) != bin &&
- (unsigned long) (victim->size) >= (unsigned long) (nb))
+ if ((victim = first (bin)) != bin
+ && (unsigned long) chunksize_nomask (victim)
+ >= (unsigned long) (nb))
{
victim = victim->bk_nextsize;
while (((unsigned long) (size = chunksize (victim)) <
@@ -3616,7 +3903,9 @@ _int_malloc (mstate av, size_t bytes)
/* Avoid removing the first entry for a size so that the skip
list does not have to be rerouted. */
- if (victim != last (bin) && victim->size == victim->fd->size)
+ if (victim != last (bin)
+ && chunksize_nomask (victim)
+ == chunksize_nomask (victim->fd))
victim = victim->fd;
remainder_size = size - nb;
@@ -3627,7 +3916,7 @@ _int_malloc (mstate av, size_t bytes)
{
set_inuse_bit_at_offset (victim, size);
if (av != &main_arena)
- victim->size |= NON_MAIN_ARENA;
+ set_non_main_arena (victim);
}
/* Split */
else
@@ -3637,11 +3926,8 @@ _int_malloc (mstate av, size_t bytes)
have to perform a complete insert here. */
bck = unsorted_chunks (av);
fwd = bck->fd;
- if (__glibc_unlikely (fwd->bk != bck))
- {
- errstr = "malloc(): corrupted unsorted chunks";
- goto errout;
- }
+ if (__glibc_unlikely (fwd->bk != bck))
+ malloc_printerr ("malloc(): corrupted unsorted chunks");
remainder->bk = bck;
remainder->fd = fwd;
bck->fd = remainder;
@@ -3732,7 +4018,7 @@ _int_malloc (mstate av, size_t bytes)
{
set_inuse_bit_at_offset (victim, size);
if (av != &main_arena)
- victim->size |= NON_MAIN_ARENA;
+ set_non_main_arena (victim);
}
/* Split */
@@ -3744,11 +4030,8 @@ _int_malloc (mstate av, size_t bytes)
have to perform a complete insert here. */
bck = unsorted_chunks (av);
fwd = bck->fd;
- if (__glibc_unlikely (fwd->bk != bck))
- {
- errstr = "malloc(): corrupted unsorted chunks 2";
- goto errout;
- }
+ if (__glibc_unlikely (fwd->bk != bck))
+ malloc_printerr ("malloc(): corrupted unsorted chunks 2");
remainder->bk = bck;
remainder->fd = fwd;
bck->fd = remainder;
@@ -3810,7 +4093,7 @@ _int_malloc (mstate av, size_t bytes)
/* When we are using atomic ops to free fast chunks we can get
here for all block sizes. */
- else if (have_fastchunks (av))
+ else if (atomic_load_relaxed (&av->have_fastchunks))
{
malloc_consolidate (av);
/* restore original bin index */
@@ -3849,9 +4132,6 @@ _int_free (mstate av, mchunkptr p, int have_lock)
mchunkptr bck; /* misc temp for linking */
mchunkptr fwd; /* misc temp for linking */
- const char *errstr = NULL;
- int locked = 0;
-
size = chunksize (p);
/* Little security check which won't hurt performance: the
@@ -3860,24 +4140,28 @@ _int_free (mstate av, mchunkptr p, int have_lock)
here by accident or by "design" from some intruder. */
if (__builtin_expect ((uintptr_t) p > (uintptr_t) -size, 0)
|| __builtin_expect (misaligned_chunk (p), 0))
- {
- errstr = "free(): invalid pointer";
- errout:
- if (!have_lock && locked)
- (void) mutex_unlock (&av->mutex);
- malloc_printerr (check_action, errstr, chunk2mem (p), av);
- return;
- }
+ malloc_printerr ("free(): invalid pointer");
/* We know that each chunk is at least MINSIZE bytes in size or a
multiple of MALLOC_ALIGNMENT. */
if (__glibc_unlikely (size < MINSIZE || !aligned_OK (size)))
- {
- errstr = "free(): invalid size";
- goto errout;
- }
+ malloc_printerr ("free(): invalid size");
check_inuse_chunk(av, p);
+#if USE_TCACHE
+ {
+ size_t tc_idx = csize2tidx (size);
+
+ if (tcache
+ && tc_idx < mp_.tcache_bins
+ && tcache->counts[tc_idx] < mp_.tcache_count)
+ {
+ tcache_put (p, tc_idx);
+ return;
+ }
+ }
+#endif
+
/*
If eligible, place chunk on a fastbin so it can be found
and used quickly in malloc.
@@ -3894,64 +4178,64 @@ _int_free (mstate av, mchunkptr p, int have_lock)
#endif
) {
- if (__builtin_expect (chunk_at_offset (p, size)->size <= 2 * SIZE_SZ, 0)
+ if (__builtin_expect (chunksize_nomask (chunk_at_offset (p, size))
+ <= 2 * SIZE_SZ, 0)
|| __builtin_expect (chunksize (chunk_at_offset (p, size))
>= av->system_mem, 0))
{
+ bool fail = true;
/* We might not have a lock at this point and concurrent modifications
- of system_mem might have let to a false positive. Redo the test
- after getting the lock. */
- if (have_lock
- || ({ assert (locked == 0);
- mutex_lock(&av->mutex);
- locked = 1;
- chunk_at_offset (p, size)->size <= 2 * SIZE_SZ
- || chunksize (chunk_at_offset (p, size)) >= av->system_mem;
- }))
+ of system_mem might result in a false positive. Redo the test after
+ getting the lock. */
+ if (!have_lock)
{
- errstr = "free(): invalid next size (fast)";
- goto errout;
- }
- if (! have_lock)
- {
- (void)mutex_unlock(&av->mutex);
- locked = 0;
+ __libc_lock_lock (av->mutex);
+ fail = (chunksize_nomask (chunk_at_offset (p, size)) <= 2 * SIZE_SZ
+ || chunksize (chunk_at_offset (p, size)) >= av->system_mem);
+ __libc_lock_unlock (av->mutex);
}
+
+ if (fail)
+ malloc_printerr ("free(): invalid next size (fast)");
}
free_perturb (chunk2mem(p), size - 2 * SIZE_SZ);
- set_fastchunks(av);
+ atomic_store_relaxed (&av->have_fastchunks, true);
unsigned int idx = fastbin_index(size);
fb = &fastbin (av, idx);
/* Atomically link P to its fastbin: P->FD = *FB; *FB = P; */
mchunkptr old = *fb, old2;
- unsigned int old_idx = ~0u;
- do
- {
- /* Check that the top of the bin is not the record we are going to add
- (i.e., double free). */
- if (__builtin_expect (old == p, 0))
- {
- errstr = "double free or corruption (fasttop)";
- goto errout;
- }
- /* Check that size of fastbin chunk at the top is the same as
- size of the chunk that we are adding. We can dereference OLD
- only if we have the lock, otherwise it might have already been
- deallocated. See use of OLD_IDX below for the actual check. */
- if (have_lock && old != NULL)
- old_idx = fastbin_index(chunksize(old));
- p->fd = old2 = old;
- }
- while ((old = catomic_compare_and_exchange_val_rel (fb, p, old2)) != old2);
- if (have_lock && old != NULL && __builtin_expect (old_idx != idx, 0))
+ if (SINGLE_THREAD_P)
{
- errstr = "invalid fastbin entry (free)";
- goto errout;
+ /* Check that the top of the bin is not the record we are going to
+ add (i.e., double free). */
+ if (__builtin_expect (old == p, 0))
+ malloc_printerr ("double free or corruption (fasttop)");
+ p->fd = old;
+ *fb = p;
}
+ else
+ do
+ {
+ /* Check that the top of the bin is not the record we are going to
+ add (i.e., double free). */
+ if (__builtin_expect (old == p, 0))
+ malloc_printerr ("double free or corruption (fasttop)");
+ p->fd = old2 = old;
+ }
+ while ((old = catomic_compare_and_exchange_val_rel (fb, p, old2))
+ != old2);
+
+ /* Check that size of fastbin chunk at the top is the same as
+ size of the chunk that we are adding. We can dereference OLD
+ only if we have the lock, otherwise it might have already been
+ allocated again. */
+ if (have_lock && old != NULL
+ && __builtin_expect (fastbin_index (chunksize (old)) != idx, 0))
+ malloc_printerr ("invalid fastbin entry (free)");
}
/*
@@ -3959,48 +4243,39 @@ _int_free (mstate av, mchunkptr p, int have_lock)
*/
else if (!chunk_is_mmapped(p)) {
- if (! have_lock) {
- (void)mutex_lock(&av->mutex);
- locked = 1;
- }
+
+ /* If we're single-threaded, don't lock the arena. */
+ if (SINGLE_THREAD_P)
+ have_lock = true;
+
+ if (!have_lock)
+ __libc_lock_lock (av->mutex);
nextchunk = chunk_at_offset(p, size);
/* Lightweight tests: check whether the block is already the
top block. */
if (__glibc_unlikely (p == av->top))
- {
- errstr = "double free or corruption (top)";
- goto errout;
- }
+ malloc_printerr ("double free or corruption (top)");
/* Or whether the next chunk is beyond the boundaries of the arena. */
if (__builtin_expect (contiguous (av)
&& (char *) nextchunk
>= ((char *) av->top + chunksize(av->top)), 0))
- {
- errstr = "double free or corruption (out)";
- goto errout;
- }
+ malloc_printerr ("double free or corruption (out)");
/* Or whether the block is actually not marked used. */
if (__glibc_unlikely (!prev_inuse(nextchunk)))
- {
- errstr = "double free or corruption (!prev)";
- goto errout;
- }
+ malloc_printerr ("double free or corruption (!prev)");
nextsize = chunksize(nextchunk);
- if (__builtin_expect (nextchunk->size <= 2 * SIZE_SZ, 0)
+ if (__builtin_expect (chunksize_nomask (nextchunk) <= 2 * SIZE_SZ, 0)
|| __builtin_expect (nextsize >= av->system_mem, 0))
- {
- errstr = "free(): invalid next size (normal)";
- goto errout;
- }
+ malloc_printerr ("free(): invalid next size (normal)");
free_perturb (chunk2mem(p), size - 2 * SIZE_SZ);
/* consolidate backward */
if (!prev_inuse(p)) {
- prevsize = p->prev_size;
+ prevsize = prev_size (p);
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
unlink(av, p, bck, fwd);
@@ -4026,10 +4301,7 @@ _int_free (mstate av, mchunkptr p, int have_lock)
bck = unsorted_chunks(av);
fwd = bck->fd;
if (__glibc_unlikely (fwd->bk != bck))
- {
- errstr = "free(): corrupted unsorted chunks";
- goto errout;
- }
+ malloc_printerr ("free(): corrupted unsorted chunks");
p->fd = fwd;
p->bk = bck;
if (!in_smallbin_range(size))
@@ -4072,7 +4344,7 @@ _int_free (mstate av, mchunkptr p, int have_lock)
*/
if ((unsigned long)(size) >= FASTBIN_CONSOLIDATION_THRESHOLD) {
- if (have_fastchunks(av))
+ if (atomic_load_relaxed (&av->have_fastchunks))
malloc_consolidate(av);
if (av == &main_arena) {
@@ -4091,10 +4363,8 @@ _int_free (mstate av, mchunkptr p, int have_lock)
}
}
- if (! have_lock) {
- assert (locked);
- (void)mutex_unlock(&av->mutex);
- }
+ if (!have_lock)
+ __libc_lock_unlock (av->mutex);
}
/*
If the chunk was allocated via mmap, release via munmap().
@@ -4113,10 +4383,6 @@ _int_free (mstate av, mchunkptr p, int have_lock)
purpose since, among other things, it might place chunks back onto
fastbins. So, instead, we need to use a minor variant of the same
code.
-
- Also, because this routine needs to be called the first time through
- malloc anyway, it turns out to be the perfect place to trigger
- initialization code.
*/
static void malloc_consolidate(mstate av)
@@ -4137,84 +4403,79 @@ static void malloc_consolidate(mstate av)
mchunkptr bck;
mchunkptr fwd;
+ atomic_store_relaxed (&av->have_fastchunks, false);
+
+ unsorted_bin = unsorted_chunks(av);
+
/*
- If max_fast is 0, we know that av hasn't
- yet been initialized, in which case do so below
+ Remove each chunk from fast bin and consolidate it, placing it
+ then in unsorted bin. Among other reasons for doing this,
+ placing in unsorted bin avoids needing to calculate actual bins
+ until malloc is sure that chunks aren't immediately going to be
+ reused anyway.
*/
- if (get_max_fast () != 0) {
- clear_fastchunks(av);
-
- unsorted_bin = unsorted_chunks(av);
+ maxfb = &fastbin (av, NFASTBINS - 1);
+ fb = &fastbin (av, 0);
+ do {
+ p = atomic_exchange_acq (fb, NULL);
+ if (p != 0) {
+ do {
+ {
+ unsigned int idx = fastbin_index (chunksize (p));
+ if ((&fastbin (av, idx)) != fb)
+ malloc_printerr ("malloc_consolidate(): invalid chunk size");
+ }
- /*
- Remove each chunk from fast bin and consolidate it, placing it
- then in unsorted bin. Among other reasons for doing this,
- placing in unsorted bin avoids needing to calculate actual bins
- until malloc is sure that chunks aren't immediately going to be
- reused anyway.
- */
+ check_inuse_chunk(av, p);
+ nextp = p->fd;
- maxfb = &fastbin (av, NFASTBINS - 1);
- fb = &fastbin (av, 0);
- do {
- p = atomic_exchange_acq (fb, 0);
- if (p != 0) {
- do {
- check_inuse_chunk(av, p);
- nextp = p->fd;
-
- /* Slightly streamlined version of consolidation code in free() */
- size = p->size & ~(PREV_INUSE|NON_MAIN_ARENA);
- nextchunk = chunk_at_offset(p, size);
- nextsize = chunksize(nextchunk);
-
- if (!prev_inuse(p)) {
- prevsize = p->prev_size;
- size += prevsize;
- p = chunk_at_offset(p, -((long) prevsize));
- unlink(av, p, bck, fwd);
- }
+ /* Slightly streamlined version of consolidation code in free() */
+ size = chunksize (p);
+ nextchunk = chunk_at_offset(p, size);
+ nextsize = chunksize(nextchunk);
- if (nextchunk != av->top) {
- nextinuse = inuse_bit_at_offset(nextchunk, nextsize);
+ if (!prev_inuse(p)) {
+ prevsize = prev_size (p);
+ size += prevsize;
+ p = chunk_at_offset(p, -((long) prevsize));
+ unlink(av, p, bck, fwd);
+ }
- if (!nextinuse) {
- size += nextsize;
- unlink(av, nextchunk, bck, fwd);
- } else
- clear_inuse_bit_at_offset(nextchunk, 0);
+ if (nextchunk != av->top) {
+ nextinuse = inuse_bit_at_offset(nextchunk, nextsize);
- first_unsorted = unsorted_bin->fd;
- unsorted_bin->fd = p;
- first_unsorted->bk = p;
+ if (!nextinuse) {
+ size += nextsize;
+ unlink(av, nextchunk, bck, fwd);
+ } else
+ clear_inuse_bit_at_offset(nextchunk, 0);
- if (!in_smallbin_range (size)) {
- p->fd_nextsize = NULL;
- p->bk_nextsize = NULL;
- }
+ first_unsorted = unsorted_bin->fd;
+ unsorted_bin->fd = p;
+ first_unsorted->bk = p;
- set_head(p, size | PREV_INUSE);
- p->bk = unsorted_bin;
- p->fd = first_unsorted;
- set_foot(p, size);
+ if (!in_smallbin_range (size)) {
+ p->fd_nextsize = NULL;
+ p->bk_nextsize = NULL;
}
- else {
- size += nextsize;
- set_head(p, size | PREV_INUSE);
- av->top = p;
- }
+ set_head(p, size | PREV_INUSE);
+ p->bk = unsorted_bin;
+ p->fd = first_unsorted;
+ set_foot(p, size);
+ }
- } while ( (p = nextp) != 0);
+ else {
+ size += nextsize;
+ set_head(p, size | PREV_INUSE);
+ av->top = p;
+ }
- }
- } while (fb++ != maxfb);
- }
- else {
- malloc_init_state(av);
- check_malloc_state(av);
- }
+ } while ( (p = nextp) != 0);
+
+ }
+ } while (fb++ != maxfb);
}
/*
@@ -4242,17 +4503,10 @@ _int_realloc(mstate av, mchunkptr oldp, INTERNAL_SIZE_T oldsize,
INTERNAL_SIZE_T* s; /* copy source */
INTERNAL_SIZE_T* d; /* copy destination */
- const char *errstr = NULL;
-
/* oldmem size */
- if (__builtin_expect (oldp->size <= 2 * SIZE_SZ, 0)
+ if (__builtin_expect (chunksize_nomask (oldp) <= 2 * SIZE_SZ, 0)
|| __builtin_expect (oldsize >= av->system_mem, 0))
- {
- errstr = "realloc(): invalid old size";
- errout:
- malloc_printerr (check_action, errstr, chunk2mem (oldp), av);
- return NULL;
- }
+ malloc_printerr ("realloc(): invalid old size");
check_inuse_chunk (av, oldp);
@@ -4261,12 +4515,9 @@ _int_realloc(mstate av, mchunkptr oldp, INTERNAL_SIZE_T oldsize,
next = chunk_at_offset (oldp, oldsize);
INTERNAL_SIZE_T nextsize = chunksize (next);
- if (__builtin_expect (next->size <= 2 * SIZE_SZ, 0)
+ if (__builtin_expect (chunksize_nomask (next) <= 2 * SIZE_SZ, 0)
|| __builtin_expect (nextsize >= av->system_mem, 0))
- {
- errstr = "realloc(): invalid next size";
- goto errout;
- }
+ malloc_printerr ("realloc(): invalid next size");
if ((unsigned long) (oldsize) >= (unsigned long) (nb))
{
@@ -4417,6 +4668,13 @@ _int_memalign (mstate av, size_t alignment, size_t bytes)
*/
+ /* Check for overflow. */
+ if (nb > SIZE_MAX - alignment - MINSIZE)
+ {
+ __set_errno (ENOMEM);
+ return 0;
+ }
+
/* Call malloc with worst case padding to hit alignment. */
m = (char *) (_int_malloc (av, nb + alignment + MINSIZE));
@@ -4447,7 +4705,7 @@ _int_memalign (mstate av, size_t alignment, size_t bytes)
/* For mmapped chunks, just adjust offset */
if (chunk_is_mmapped (p))
{
- newp->prev_size = p->prev_size + leadsize;
+ set_prev_size (newp, prev_size (p) + leadsize);
set_head (newp, newsize | IS_MMAPPED);
return chunk2mem (newp);
}
@@ -4491,11 +4749,7 @@ _int_memalign (mstate av, size_t alignment, size_t bytes)
static int
mtrim (mstate av, size_t pad)
{
- /* Don't touch corrupt arenas. */
- if (arena_is_corrupt (av))
- return 0;
-
- /* Ensure initialization/consolidation */
+ /* Ensure all blocks are consolidated. */
malloc_consolidate (av);
const size_t ps = GLRO (dl_pagesize);
@@ -4560,9 +4814,9 @@ __malloc_trim (size_t s)
mstate ar_ptr = &main_arena;
do
{
- (void) mutex_lock (&ar_ptr->mutex);
+ __libc_lock_lock (ar_ptr->mutex);
result |= mtrim (ar_ptr, s);
- (void) mutex_unlock (&ar_ptr->mutex);
+ __libc_lock_unlock (ar_ptr->mutex);
ar_ptr = ar_ptr->next;
}
@@ -4588,7 +4842,12 @@ musable (void *mem)
return malloc_check_get_size (p);
if (chunk_is_mmapped (p))
- return chunksize (p) - 2 * SIZE_SZ;
+ {
+ if (DUMPED_MAIN_ARENA_CHUNK (p))
+ return chunksize (p) - SIZE_SZ;
+ else
+ return chunksize (p) - 2 * SIZE_SZ;
+ }
else if (inuse (p))
return chunksize (p) - SIZE_SZ;
}
@@ -4621,10 +4880,6 @@ int_mallinfo (mstate av, struct mallinfo *m)
int nblocks;
int nfastblocks;
- /* Ensure initialization */
- if (av->top == 0)
- malloc_consolidate (av);
-
check_malloc_state (av);
/* Account for top */
@@ -4667,7 +4922,7 @@ int_mallinfo (mstate av, struct mallinfo *m)
{
m->hblks = mp_.n_mmaps;
m->hblkhd = mp_.mmapped_mem;
- m->usmblks = mp_.max_total_mem;
+ m->usmblks = 0;
m->keepcost = chunksize (av->top);
}
}
@@ -4686,9 +4941,9 @@ __libc_mallinfo (void)
ar_ptr = &main_arena;
do
{
- (void) mutex_lock (&ar_ptr->mutex);
+ __libc_lock_lock (ar_ptr->mutex);
int_mallinfo (ar_ptr, &m);
- (void) mutex_unlock (&ar_ptr->mutex);
+ __libc_lock_unlock (ar_ptr->mutex);
ar_ptr = ar_ptr->next;
}
@@ -4711,14 +4966,14 @@ __malloc_stats (void)
if (__malloc_initialized < 0)
ptmalloc_init ();
_IO_flockfile (stderr);
- int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
- ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+ int old_flags2 = stderr->_flags2;
+ stderr->_flags2 |= _IO_FLAGS2_NOTCANCEL;
for (i = 0, ar_ptr = &main_arena;; i++)
{
struct mallinfo mi;
memset (&mi, 0, sizeof (mi));
- (void) mutex_lock (&ar_ptr->mutex);
+ __libc_lock_lock (ar_ptr->mutex);
int_mallinfo (ar_ptr, &mi);
fprintf (stderr, "Arena %d:\n", i);
fprintf (stderr, "system bytes = %10u\n", (unsigned int) mi.arena);
@@ -4729,7 +4984,7 @@ __malloc_stats (void)
#endif
system_b += mi.arena;
in_use_b += mi.uordblks;
- (void) mutex_unlock (&ar_ptr->mutex);
+ __libc_lock_unlock (ar_ptr->mutex);
ar_ptr = ar_ptr->next;
if (ar_ptr == &main_arena)
break;
@@ -4740,7 +4995,7 @@ __malloc_stats (void)
fprintf (stderr, "max mmap regions = %10u\n", (unsigned int) mp_.max_n_mmaps);
fprintf (stderr, "max mmap bytes = %10lu\n",
(unsigned long) mp_.max_mmapped_mem);
- ((_IO_FILE *) stderr)->_flags2 |= old_flags2;
+ stderr->_flags2 = old_flags2;
_IO_funlockfile (stderr);
}
@@ -4748,6 +5003,121 @@ __malloc_stats (void)
/*
------------------------------ mallopt ------------------------------
*/
+static inline int
+__always_inline
+do_set_trim_threshold (size_t value)
+{
+ LIBC_PROBE (memory_mallopt_trim_threshold, 3, value, mp_.trim_threshold,
+ mp_.no_dyn_threshold);
+ mp_.trim_threshold = value;
+ mp_.no_dyn_threshold = 1;
+ return 1;
+}
+
+static inline int
+__always_inline
+do_set_top_pad (size_t value)
+{
+ LIBC_PROBE (memory_mallopt_top_pad, 3, value, mp_.top_pad,
+ mp_.no_dyn_threshold);
+ mp_.top_pad = value;
+ mp_.no_dyn_threshold = 1;
+ return 1;
+}
+
+static inline int
+__always_inline
+do_set_mmap_threshold (size_t value)
+{
+ /* Forbid setting the threshold too high. */
+ if (value <= HEAP_MAX_SIZE / 2)
+ {
+ LIBC_PROBE (memory_mallopt_mmap_threshold, 3, value, mp_.mmap_threshold,
+ mp_.no_dyn_threshold);
+ mp_.mmap_threshold = value;
+ mp_.no_dyn_threshold = 1;
+ return 1;
+ }
+ return 0;
+}
+
+static inline int
+__always_inline
+do_set_mmaps_max (int32_t value)
+{
+ LIBC_PROBE (memory_mallopt_mmap_max, 3, value, mp_.n_mmaps_max,
+ mp_.no_dyn_threshold);
+ mp_.n_mmaps_max = value;
+ mp_.no_dyn_threshold = 1;
+ return 1;
+}
+
+static inline int
+__always_inline
+do_set_mallopt_check (int32_t value)
+{
+ return 1;
+}
+
+static inline int
+__always_inline
+do_set_perturb_byte (int32_t value)
+{
+ LIBC_PROBE (memory_mallopt_perturb, 2, value, perturb_byte);
+ perturb_byte = value;
+ return 1;
+}
+
+static inline int
+__always_inline
+do_set_arena_test (size_t value)
+{
+ LIBC_PROBE (memory_mallopt_arena_test, 2, value, mp_.arena_test);
+ mp_.arena_test = value;
+ return 1;
+}
+
+static inline int
+__always_inline
+do_set_arena_max (size_t value)
+{
+ LIBC_PROBE (memory_mallopt_arena_max, 2, value, mp_.arena_max);
+ mp_.arena_max = value;
+ return 1;
+}
+
+#if USE_TCACHE
+static inline int
+__always_inline
+do_set_tcache_max (size_t value)
+{
+ if (value >= 0 && value <= MAX_TCACHE_SIZE)
+ {
+ LIBC_PROBE (memory_tunable_tcache_max_bytes, 2, value, mp_.tcache_max_bytes);
+ mp_.tcache_max_bytes = value;
+ mp_.tcache_bins = csize2tidx (request2size(value)) + 1;
+ }
+ return 1;
+}
+
+static inline int
+__always_inline
+do_set_tcache_count (size_t value)
+{
+ LIBC_PROBE (memory_tunable_tcache_count, 2, value, mp_.tcache_count);
+ mp_.tcache_count = value;
+ return 1;
+}
+
+static inline int
+__always_inline
+do_set_tcache_unsorted_limit (size_t value)
+{
+ LIBC_PROBE (memory_tunable_tcache_unsorted_limit, 2, value, mp_.tcache_unsorted_limit);
+ mp_.tcache_unsorted_limit = value;
+ return 1;
+}
+#endif
int
__libc_mallopt (int param_number, int value)
@@ -4757,12 +5127,14 @@ __libc_mallopt (int param_number, int value)
if (__malloc_initialized < 0)
ptmalloc_init ();
- (void) mutex_lock (&av->mutex);
- /* Ensure initialization/consolidation */
- malloc_consolidate (av);
+ __libc_lock_lock (av->mutex);
LIBC_PROBE (memory_mallopt, 2, param_number, value);
+ /* We must consolidate main arena before changing max_fast
+ (see definition of set_max_fast). */
+ malloc_consolidate (av);
+
switch (param_number)
{
case M_MXFAST:
@@ -4776,66 +5148,40 @@ __libc_mallopt (int param_number, int value)
break;
case M_TRIM_THRESHOLD:
- LIBC_PROBE (memory_mallopt_trim_threshold, 3, value,
- mp_.trim_threshold, mp_.no_dyn_threshold);
- mp_.trim_threshold = value;
- mp_.no_dyn_threshold = 1;
+ do_set_trim_threshold (value);
break;
case M_TOP_PAD:
- LIBC_PROBE (memory_mallopt_top_pad, 3, value,
- mp_.top_pad, mp_.no_dyn_threshold);
- mp_.top_pad = value;
- mp_.no_dyn_threshold = 1;
+ do_set_top_pad (value);
break;
case M_MMAP_THRESHOLD:
- /* Forbid setting the threshold too high. */
- if ((unsigned long) value > HEAP_MAX_SIZE / 2)
- res = 0;
- else
- {
- LIBC_PROBE (memory_mallopt_mmap_threshold, 3, value,
- mp_.mmap_threshold, mp_.no_dyn_threshold);
- mp_.mmap_threshold = value;
- mp_.no_dyn_threshold = 1;
- }
+ res = do_set_mmap_threshold (value);
break;
case M_MMAP_MAX:
- LIBC_PROBE (memory_mallopt_mmap_max, 3, value,
- mp_.n_mmaps_max, mp_.no_dyn_threshold);
- mp_.n_mmaps_max = value;
- mp_.no_dyn_threshold = 1;
+ do_set_mmaps_max (value);
break;
case M_CHECK_ACTION:
- LIBC_PROBE (memory_mallopt_check_action, 2, value, check_action);
- check_action = value;
+ do_set_mallopt_check (value);
break;
case M_PERTURB:
- LIBC_PROBE (memory_mallopt_perturb, 2, value, perturb_byte);
- perturb_byte = value;
+ do_set_perturb_byte (value);
break;
case M_ARENA_TEST:
if (value > 0)
- {
- LIBC_PROBE (memory_mallopt_arena_test, 2, value, mp_.arena_test);
- mp_.arena_test = value;
- }
+ do_set_arena_test (value);
break;
case M_ARENA_MAX:
if (value > 0)
- {
- LIBC_PROBE (memory_mallopt_arena_max, 2, value, mp_.arena_max);
- mp_.arena_max = value;
- }
+ do_set_arena_max (value);
break;
}
- (void) mutex_unlock (&av->mutex);
+ __libc_lock_unlock (av->mutex);
return res;
}
libc_hidden_def (__libc_mallopt)
@@ -4985,30 +5331,10 @@ libc_hidden_def (__libc_mallopt)
extern char **__libc_argv attribute_hidden;
static void
-malloc_printerr (int action, const char *str, void *ptr, mstate ar_ptr)
+malloc_printerr (const char *str)
{
- /* Avoid using this arena in future. We do not attempt to synchronize this
- with anything else because we minimally want to ensure that __libc_message
- gets its resources safely without stumbling on the current corruption. */
- if (ar_ptr)
- set_arena_corrupt (ar_ptr);
-
- if ((action & 5) == 5)
- __libc_message (action & 2, "%s\n", str);
- else if (action & 1)
- {
- char buf[2 * sizeof (uintptr_t) + 1];
-
- buf[sizeof (buf) - 1] = '\0';
- char *cp = _itoa_word ((uintptr_t) ptr, &buf[sizeof (buf) - 1], 16, 0);
- while (cp > buf)
- *--cp = '0';
-
- __libc_message (action & 2, "*** Error in `%s': %s: 0x%s ***\n",
- __libc_argv[0] ? : "<unknown>", str, cp);
- }
- else if (action & 2)
- abort ();
+ __libc_message (do_abort, "%s\n", str);
+ __builtin_unreachable ();
}
/* We need a wrapper function for one of the additions of POSIX. */
@@ -5082,7 +5408,7 @@ __malloc_info (int options, FILE *fp)
} sizes[NFASTBINS + NBINS - 1];
#define nsizes (sizeof (sizes) / sizeof (sizes[0]))
- mutex_lock (&ar_ptr->mutex);
+ __libc_lock_lock (ar_ptr->mutex);
for (size_t i = 0; i < NFASTBINS; ++i)
{
@@ -5125,12 +5451,13 @@ __malloc_info (int options, FILE *fp)
if (r != NULL)
while (r != bin)
{
+ size_t r_size = chunksize_nomask (r);
++sizes[NFASTBINS - 1 + i].count;
- sizes[NFASTBINS - 1 + i].total += r->size;
+ sizes[NFASTBINS - 1 + i].total += r_size;
sizes[NFASTBINS - 1 + i].from
- = MIN (sizes[NFASTBINS - 1 + i].from, r->size);
+ = MIN (sizes[NFASTBINS - 1 + i].from, r_size);
sizes[NFASTBINS - 1 + i].to = MAX (sizes[NFASTBINS - 1 + i].to,
- r->size);
+ r_size);
r = r->fd;
}
@@ -5141,7 +5468,24 @@ __malloc_info (int options, FILE *fp)
avail += sizes[NFASTBINS - 1 + i].total;
}
- mutex_unlock (&ar_ptr->mutex);
+ size_t heap_size = 0;
+ size_t heap_mprotect_size = 0;
+ size_t heap_count = 0;
+ if (ar_ptr != &main_arena)
+ {
+ /* Iterate over the arena heaps from back to front. */
+ heap_info *heap = heap_for_ptr (top (ar_ptr));
+ do
+ {
+ heap_size += heap->size;
+ heap_mprotect_size += heap->mprotect_size;
+ heap = heap->prev;
+ ++heap_count;
+ }
+ while (heap != NULL);
+ }
+
+ __libc_lock_unlock (ar_ptr->mutex);
total_nfastblocks += nfastblocks;
total_fastavail += fastavail;
@@ -5174,13 +5518,13 @@ __malloc_info (int options, FILE *fp)
if (ar_ptr != &main_arena)
{
- heap_info *heap = heap_for_ptr (top (ar_ptr));
fprintf (fp,
"<aspace type=\"total\" size=\"%zu\"/>\n"
- "<aspace type=\"mprotect\" size=\"%zu\"/>\n",
- heap->size, heap->mprotect_size);
- total_aspace += heap->size;
- total_aspace_mprotect += heap->mprotect_size;
+ "<aspace type=\"mprotect\" size=\"%zu\"/>\n"
+ "<aspace type=\"subheaps\" size=\"%zu\"/>\n",
+ heap_size, heap_mprotect_size, heap_count);
+ total_aspace += heap_size;
+ total_aspace_mprotect += heap_mprotect_size;
}
else
{
@@ -5217,7 +5561,6 @@ weak_alias (__malloc_info, malloc_info)
strong_alias (__libc_calloc, __calloc) weak_alias (__libc_calloc, calloc)
-strong_alias (__libc_free, __cfree) weak_alias (__libc_free, cfree)
strong_alias (__libc_free, __free) strong_alias (__libc_free, free)
strong_alias (__libc_malloc, __malloc) strong_alias (__libc_malloc, malloc)
strong_alias (__libc_memalign, __memalign)
@@ -5232,9 +5575,10 @@ strong_alias (__libc_mallopt, __mallopt) weak_alias (__libc_mallopt, mallopt)
weak_alias (__malloc_stats, malloc_stats)
weak_alias (__malloc_usable_size, malloc_usable_size)
weak_alias (__malloc_trim, malloc_trim)
-weak_alias (__malloc_get_state, malloc_get_state)
-weak_alias (__malloc_set_state, malloc_set_state)
+#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_26)
+compat_symbol (libc, __libc_free, cfree, GLIBC_2_0);
+#endif
/* ------------------------------------------------------------
History:
diff --git a/malloc/malloc.h b/malloc/malloc.h
index f65e18aa82..3e7c447be1 100644
--- a/malloc/malloc.h
+++ b/malloc/malloc.h
@@ -1,5 +1,5 @@
/* Prototypes and definition for malloc implementation.
- Copyright (C) 1996-2016 Free Software Foundation, Inc.
+ Copyright (C) 1996-2018 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
@@ -49,12 +49,17 @@ __THROW __attribute_malloc__ __wur;
extern void *realloc (void *__ptr, size_t __size)
__THROW __attribute_warn_unused_result__;
+/* Re-allocate the previously allocated block in PTR, making the new
+ block large enough for NMEMB elements of SIZE bytes each. */
+/* __attribute_malloc__ is not used, because if reallocarray returns
+ the same pointer that was passed to it, aliasing needs to be allowed
+ between objects pointed by the old and new pointers. */
+extern void *reallocarray (void *__ptr, size_t __nmemb, size_t __size)
+__THROW __attribute_warn_unused_result__;
+
/* Free a block allocated by `malloc', `realloc' or `calloc'. */
extern void free (void *__ptr) __THROW;
-/* Free a block allocated by `calloc'. */
-extern void cfree (void *__ptr) __THROW;
-
/* Allocate SIZE bytes allocated to ALIGNMENT bytes. */
extern void *memalign (size_t __alignment, size_t __size)
__THROW __attribute_malloc__ __wur;
@@ -83,7 +88,7 @@ struct mallinfo
int smblks; /* number of fastbin blocks */
int hblks; /* number of mmapped regions */
int hblkhd; /* space in mmapped regions */
- int usmblks; /* maximum total allocated space */
+ int usmblks; /* always 0, preserved for backwards compatibility */
int fsmblks; /* space available in freed fastbin blocks */
int uordblks; /* total allocated space */
int fordblks; /* total free space */
@@ -134,18 +139,6 @@ extern void malloc_stats (void) __THROW;
/* Output information about state of allocator to stream FP. */
extern int malloc_info (int __options, FILE *__fp) __THROW;
-/* Record the state of all malloc variables in an opaque data structure. */
-extern void *malloc_get_state (void) __THROW;
-
-/* Restore the state of all malloc variables from data obtained with
- malloc_get_state(). */
-extern int malloc_set_state (void *__ptr) __THROW;
-
-/* Called once when malloc is initialized; redefining this variable in
- the application provides the preferred way to set up the hook
- pointers. */
-extern void (*__MALLOC_HOOK_VOLATILE __malloc_initialize_hook) (void)
-__MALLOC_DEPRECATED;
/* Hooks for debugging and user-defined versions. */
extern void (*__MALLOC_HOOK_VOLATILE __free_hook) (void *__ptr,
const void *)
diff --git a/malloc/mcheck-init.c b/malloc/mcheck-init.c
index 8d63dd3488..92cb80ff2c 100644
--- a/malloc/mcheck-init.c
+++ b/malloc/mcheck-init.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991-2016 Free Software Foundation, Inc.
+/* Copyright (C) 1991-2018 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
@@ -20,6 +20,7 @@
#include <malloc.h>
#include <mcheck.h>
+#include <shlib-compat.h>
static void
turn_on_mcheck (void)
@@ -28,3 +29,5 @@ turn_on_mcheck (void)
}
void (*__malloc_initialize_hook) (void) = turn_on_mcheck;
+compat_symbol_reference (libc, __malloc_initialize_hook,
+ __malloc_initialize_hook, GLIBC_2_0);
diff --git a/malloc/mcheck.c b/malloc/mcheck.c
index 73491b1c5a..dc04a6391a 100644
--- a/malloc/mcheck.c
+++ b/malloc/mcheck.c
@@ -1,5 +1,5 @@
/* Standard debugging hooks for `malloc'.
- Copyright (C) 1990-2016 Free Software Foundation, Inc.
+ Copyright (C) 1990-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Written May 1989 by Mike Haertel.
@@ -28,12 +28,12 @@
#endif
/* Old hook values. */
-static void (*old_free_hook)(__ptr_t ptr, const __ptr_t);
-static __ptr_t (*old_malloc_hook) (size_t size, const __ptr_t);
-static __ptr_t (*old_memalign_hook) (size_t alignment, size_t size,
- const __ptr_t);
-static __ptr_t (*old_realloc_hook) (__ptr_t ptr, size_t size,
- const __ptr_t);
+static void (*old_free_hook)(void *ptr, const void *);
+static void *(*old_malloc_hook) (size_t size, const void *);
+static void *(*old_memalign_hook) (size_t alignment, size_t size,
+ const void *);
+static void *(*old_realloc_hook) (void *ptr, size_t size,
+ const void *);
/* Function to call when something awful happens. */
static void (*abortfunc) (enum mcheck_status);
@@ -51,7 +51,7 @@ struct hdr
unsigned long int magic; /* Magic number to check header integrity. */
struct hdr *prev;
struct hdr *next;
- __ptr_t block; /* Real block allocated, for memalign. */
+ void *block; /* Real block allocated, for memalign. */
unsigned long int magic2; /* Extra, keeps us doubleword aligned. */
};
@@ -68,9 +68,9 @@ static int pedantic;
# include <string.h>
# define flood memset
#else
-static void flood (__ptr_t, int, size_t);
+static void flood (void *, int, size_t);
static void
-flood (__ptr_t ptr, int val, size_t size)
+flood (void *ptr, int val, size_t size)
{
char *cp = ptr;
while (size--)
@@ -174,7 +174,7 @@ link_blk (struct hdr *hdr)
}
}
static void
-freehook (__ptr_t ptr, const __ptr_t caller)
+freehook (void *ptr, const void *caller)
{
if (pedantic)
mcheck_check_all ();
@@ -197,8 +197,8 @@ freehook (__ptr_t ptr, const __ptr_t caller)
__free_hook = freehook;
}
-static __ptr_t
-mallochook (size_t size, const __ptr_t caller)
+static void *
+mallochook (size_t size, const void *caller)
{
struct hdr *hdr;
@@ -226,13 +226,13 @@ mallochook (size_t size, const __ptr_t caller)
hdr->block = hdr;
hdr->magic2 = (uintptr_t) hdr ^ MAGICWORD;
((char *) &hdr[1])[size] = MAGICBYTE;
- flood ((__ptr_t) (hdr + 1), MALLOCFLOOD, size);
- return (__ptr_t) (hdr + 1);
+ flood ((void *) (hdr + 1), MALLOCFLOOD, size);
+ return (void *) (hdr + 1);
}
-static __ptr_t
+static void *
memalignhook (size_t alignment, size_t size,
- const __ptr_t caller)
+ const void *caller)
{
struct hdr *hdr;
size_t slop;
@@ -262,15 +262,15 @@ memalignhook (size_t alignment, size_t size,
hdr->size = size;
link_blk (hdr);
- hdr->block = (__ptr_t) block;
+ hdr->block = (void *) block;
hdr->magic2 = (uintptr_t) block ^ MAGICWORD;
((char *) &hdr[1])[size] = MAGICBYTE;
- flood ((__ptr_t) (hdr + 1), MALLOCFLOOD, size);
- return (__ptr_t) (hdr + 1);
+ flood ((void *) (hdr + 1), MALLOCFLOOD, size);
+ return (void *) (hdr + 1);
}
-static __ptr_t
-reallochook (__ptr_t ptr, size_t size, const __ptr_t caller)
+static void *
+reallochook (void *ptr, size_t size, const void *caller)
{
if (size == 0)
{
@@ -310,11 +310,11 @@ reallochook (__ptr_t ptr, size_t size, const __ptr_t caller)
__memalign_hook = old_memalign_hook;
__realloc_hook = old_realloc_hook;
if (old_realloc_hook != NULL)
- hdr = (struct hdr *) (*old_realloc_hook)((__ptr_t) hdr,
+ hdr = (struct hdr *) (*old_realloc_hook)((void *) hdr,
sizeof (struct hdr) + size + 1,
caller);
else
- hdr = (struct hdr *) realloc ((__ptr_t) hdr,
+ hdr = (struct hdr *) realloc ((void *) hdr,
sizeof (struct hdr) + size + 1);
__free_hook = freehook;
__malloc_hook = mallochook;
@@ -330,7 +330,7 @@ reallochook (__ptr_t ptr, size_t size, const __ptr_t caller)
((char *) &hdr[1])[size] = MAGICBYTE;
if (size > osize)
flood ((char *) (hdr + 1) + osize, MALLOCFLOOD, size - osize);
- return (__ptr_t) (hdr + 1);
+ return (void *) (hdr + 1);
}
__attribute__ ((noreturn))
@@ -410,7 +410,7 @@ mcheck_pedantic (void (*func) (enum mcheck_status))
}
enum mcheck_status
-mprobe (__ptr_t ptr)
+mprobe (void *ptr)
{
return mcheck_used ? checkhdr (((struct hdr *) ptr) - 1) : MCHECK_DISABLED;
}
diff --git a/malloc/mcheck.h b/malloc/mcheck.h
index 416fcd6dda..7d3b3f07ab 100644
--- a/malloc/mcheck.h
+++ b/malloc/mcheck.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 1996-2016 Free Software Foundation, Inc.
+/* Copyright (C) 1996-2018 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
diff --git a/malloc/memusage.c b/malloc/memusage.c
index 3bf0b9dd9f..9151c80121 100644
--- a/malloc/memusage.c
+++ b/malloc/memusage.c
@@ -1,5 +1,5 @@
/* Profile heap and stack memory usage of running program.
- Copyright (C) 1998-2016 Free Software Foundation, Inc.
+ Copyright (C) 1998-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
diff --git a/malloc/memusage.sh b/malloc/memusage.sh
index 7430f8881d..bc478e89fe 100755
--- a/malloc/memusage.sh
+++ b/malloc/memusage.sh
@@ -1,5 +1,5 @@
#! @BASH@
-# Copyright (C) 1999-2016 Free Software Foundation, Inc.
+# Copyright (C) 1999-2018 Free Software Foundation, Inc.
# This file is part of the GNU C Library.
# Contributed by Ulrich Drepper <drepper@gnu.org>, 1999.
@@ -71,7 +71,7 @@ do_version() {
printf $"Copyright (C) %s Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-" "2016"
+" "2018"
printf $"Written by %s.
" "Ulrich Drepper"
exit 0
diff --git a/malloc/memusagestat.c b/malloc/memusagestat.c
index 7fb65d1d97..d9b48e615d 100644
--- a/malloc/memusagestat.c
+++ b/malloc/memusagestat.c
@@ -1,5 +1,5 @@
/* Generate graphic from memory profiling data.
- Copyright (C) 1998-2016 Free Software Foundation, Inc.
+ Copyright (C) 1998-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
@@ -582,6 +582,6 @@ print_version (FILE *stream, struct argp_state *state)
Copyright (C) %s Free Software Foundation, Inc.\n\
This is free software; see the source for copying conditions. There is NO\n\
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
-"), "2016");
+"), "2018");
fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
}
diff --git a/malloc/morecore.c b/malloc/morecore.c
index 9e069ac745..165de7e386 100644
--- a/malloc/morecore.c
+++ b/malloc/morecore.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991-2016 Free Software Foundation, Inc.
+/* Copyright (C) 1991-2018 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
diff --git a/malloc/mtrace.c b/malloc/mtrace.c
index 501f014d78..9064f209ec 100644
--- a/malloc/mtrace.c
+++ b/malloc/mtrace.c
@@ -1,5 +1,5 @@
/* More debugging hooks for `malloc'.
- Copyright (C) 1991-2016 Free Software Foundation, Inc.
+ Copyright (C) 1991-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Written April 2, 1991 by John Gilmore of Cygnus Support.
Based on mcheck.c by Mike Haertel.
@@ -34,6 +34,7 @@
#include <_itoa.h>
#include <libc-internal.h>
+#include <dso_handle.h>
#include <libio/iolibio.h>
#define setvbuf(s, b, f, l) _IO_setvbuf (s, b, f, l)
@@ -50,15 +51,15 @@ static char *malloc_trace_buffer;
__libc_lock_define_initialized (static, lock);
/* Address to breakpoint on accesses to... */
-__ptr_t mallwatch;
+void *mallwatch;
/* Old hook values. */
-static void (*tr_old_free_hook) (__ptr_t ptr, const __ptr_t);
-static __ptr_t (*tr_old_malloc_hook) (size_t size, const __ptr_t);
-static __ptr_t (*tr_old_realloc_hook) (__ptr_t ptr, size_t size,
- const __ptr_t);
-static __ptr_t (*tr_old_memalign_hook) (size_t __alignment, size_t __size,
- const __ptr_t);
+static void (*tr_old_free_hook) (void *ptr, const void *);
+static void *(*tr_old_malloc_hook) (size_t size, const void *);
+static void *(*tr_old_realloc_hook) (void *ptr, size_t size,
+ const void *);
+static void *(*tr_old_memalign_hook) (size_t __alignment, size_t __size,
+ const void *);
/* This function is called when the block being alloc'd, realloc'd, or
freed has an address matching the variable "mallwatch". In a debugger,
@@ -73,8 +74,8 @@ tr_break (void)
}
libc_hidden_def (tr_break)
-static void internal_function
-tr_where (const __ptr_t caller, Dl_info *info)
+static void
+tr_where (const void *caller, Dl_info *info)
{
if (caller != NULL)
{
@@ -87,12 +88,12 @@ tr_where (const __ptr_t caller, Dl_info *info)
buf = alloca (len + 6 + 2 * sizeof (void *));
buf[0] = '(';
- __stpcpy (_fitoa (caller >= (const __ptr_t) info->dli_saddr
- ? caller - (const __ptr_t) info->dli_saddr
- : (const __ptr_t) info->dli_saddr - caller,
+ __stpcpy (_fitoa (caller >= (const void *) info->dli_saddr
+ ? caller - (const void *) info->dli_saddr
+ : (const void *) info->dli_saddr - caller,
__stpcpy (__mempcpy (buf + 1, info->dli_sname,
len),
- caller >= (__ptr_t) info->dli_saddr
+ caller >= (void *) info->dli_saddr
? "+0x" : "-0x"),
16, 0),
")");
@@ -108,7 +109,7 @@ tr_where (const __ptr_t caller, Dl_info *info)
}
static Dl_info *
-lock_and_info (const __ptr_t caller, Dl_info *mem)
+lock_and_info (const void *caller, Dl_info *mem)
{
if (caller == NULL)
return NULL;
@@ -121,7 +122,7 @@ lock_and_info (const __ptr_t caller, Dl_info *mem)
}
static void
-tr_freehook (__ptr_t ptr, const __ptr_t caller)
+tr_freehook (void *ptr, const void *caller)
{
if (ptr == NULL)
return;
@@ -146,19 +147,19 @@ tr_freehook (__ptr_t ptr, const __ptr_t caller)
__libc_lock_unlock (lock);
}
-static __ptr_t
-tr_mallochook (size_t size, const __ptr_t caller)
+static void *
+tr_mallochook (size_t size, const void *caller)
{
- __ptr_t hdr;
+ void *hdr;
Dl_info mem;
Dl_info *info = lock_and_info (caller, &mem);
__malloc_hook = tr_old_malloc_hook;
if (tr_old_malloc_hook != NULL)
- hdr = (__ptr_t) (*tr_old_malloc_hook)(size, caller);
+ hdr = (void *) (*tr_old_malloc_hook)(size, caller);
else
- hdr = (__ptr_t) malloc (size);
+ hdr = (void *) malloc (size);
__malloc_hook = tr_mallochook;
tr_where (caller, info);
@@ -173,10 +174,10 @@ tr_mallochook (size_t size, const __ptr_t caller)
return hdr;
}
-static __ptr_t
-tr_reallochook (__ptr_t ptr, size_t size, const __ptr_t caller)
+static void *
+tr_reallochook (void *ptr, size_t size, const void *caller)
{
- __ptr_t hdr;
+ void *hdr;
if (ptr == mallwatch)
tr_break ();
@@ -188,9 +189,9 @@ tr_reallochook (__ptr_t ptr, size_t size, const __ptr_t caller)
__malloc_hook = tr_old_malloc_hook;
__realloc_hook = tr_old_realloc_hook;
if (tr_old_realloc_hook != NULL)
- hdr = (__ptr_t) (*tr_old_realloc_hook)(ptr, size, caller);
+ hdr = (void *) (*tr_old_realloc_hook)(ptr, size, caller);
else
- hdr = (__ptr_t) realloc (ptr, size);
+ hdr = (void *) realloc (ptr, size);
__free_hook = tr_freehook;
__malloc_hook = tr_mallochook;
__realloc_hook = tr_reallochook;
@@ -221,10 +222,10 @@ tr_reallochook (__ptr_t ptr, size_t size, const __ptr_t caller)
return hdr;
}
-static __ptr_t
-tr_memalignhook (size_t alignment, size_t size, const __ptr_t caller)
+static void *
+tr_memalignhook (size_t alignment, size_t size, const void *caller)
{
- __ptr_t hdr;
+ void *hdr;
Dl_info mem;
Dl_info *info = lock_and_info (caller, &mem);
@@ -232,9 +233,9 @@ tr_memalignhook (size_t alignment, size_t size, const __ptr_t caller)
__memalign_hook = tr_old_memalign_hook;
__malloc_hook = tr_old_malloc_hook;
if (tr_old_memalign_hook != NULL)
- hdr = (__ptr_t) (*tr_old_memalign_hook)(alignment, size, caller);
+ hdr = (void *) (*tr_old_memalign_hook)(alignment, size, caller);
else
- hdr = (__ptr_t) memalign (alignment, size);
+ hdr = (void *) memalign (alignment, size);
__memalign_hook = tr_memalignhook;
__malloc_hook = tr_mallochook;
@@ -300,15 +301,6 @@ mtrace (void)
mallstream = fopen (mallfile != NULL ? mallfile : "/dev/null", "wce");
if (mallstream != NULL)
{
-#ifndef __ASSUME_O_CLOEXEC
- /* Make sure we close the file descriptor on exec. */
- int flags = __fcntl (fileno (mallstream), F_GETFD, 0);
- if (flags >= 0)
- {
- flags |= FD_CLOEXEC;
- __fcntl (fileno (mallstream), F_SETFD, flags);
- }
-#endif
/* Be sure it doesn't malloc its buffer! */
malloc_trace_buffer = mtb;
setvbuf (mallstream, malloc_trace_buffer, _IOFBF, TRACE_BUFFER_SIZE);
@@ -324,10 +316,9 @@ mtrace (void)
#ifdef _LIBC
if (!added_atexit_handler)
{
- extern void *__dso_handle __attribute__ ((__weak__));
added_atexit_handler = 1;
__cxa_atexit ((void (*)(void *))release_libc_mem, NULL,
- &__dso_handle ? __dso_handle : NULL);
+ __dso_handle);
}
#endif
}
diff --git a/malloc/mtrace.pl b/malloc/mtrace.pl
index 3715a7f830..fe9f546000 100644
--- a/malloc/mtrace.pl
+++ b/malloc/mtrace.pl
@@ -1,7 +1,7 @@
#! @PERL@
eval "exec @PERL@ -S $0 $@"
if 0;
-# Copyright (C) 1997-2016 Free Software Foundation, Inc.
+# Copyright (C) 1997-2018 Free Software Foundation, Inc.
# This file is part of the GNU C Library.
# Contributed by Ulrich Drepper <drepper@gnu.org>, 1997.
# Based on the mtrace.awk script.
@@ -45,7 +45,7 @@ arglist: while (@ARGV) {
$ARGV[0] eq "--vers" || $ARGV[0] eq "--versi" ||
$ARGV[0] eq "--versio" || $ARGV[0] eq "--version") {
print "mtrace $PKGVERSION$VERSION\n";
- print "Copyright (C) 2016 Free Software Foundation, Inc.\n";
+ print "Copyright (C) 2018 Free Software Foundation, Inc.\n";
print "This is free software; see the source for copying conditions. There is NO\n";
print "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n";
print "Written by Ulrich Drepper <drepper\@gnu.org>\n";
diff --git a/malloc/obstack.c b/malloc/obstack.c
index c0927bb209..05ad7e962c 100644
--- a/malloc/obstack.c
+++ b/malloc/obstack.c
@@ -1,5 +1,5 @@
/* obstack.c - subroutines used implicitly by object stack macros
- Copyright (C) 1988-2016 Free Software Foundation, Inc.
+ Copyright (C) 1988-2018 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
diff --git a/malloc/obstack.h b/malloc/obstack.h
index 5716202a66..998e4f49f8 100644
--- a/malloc/obstack.h
+++ b/malloc/obstack.h
@@ -1,5 +1,5 @@
/* obstack.h - object stack macros
- Copyright (C) 1988-2016 Free Software Foundation, Inc.
+ Copyright (C) 1988-2018 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
diff --git a/malloc/reallocarray.c b/malloc/reallocarray.c
new file mode 100644
index 0000000000..319eccd21f
--- /dev/null
+++ b/malloc/reallocarray.c
@@ -0,0 +1,37 @@
+/* Change the size of an allocated block.
+ Copyright (C) 2017-2018 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 <errno.h>
+#include <malloc.h>
+#include <malloc/malloc-internal.h>
+
+void *
+__libc_reallocarray (void *optr, size_t nmemb, size_t elem_size)
+{
+ size_t bytes;
+ if (check_mul_overflow_size_t (nmemb, elem_size, &bytes))
+ {
+ __set_errno (ENOMEM);
+ return 0;
+ }
+ else
+ return realloc (optr, bytes);
+}
+libc_hidden_def (__libc_reallocarray)
+
+weak_alias (__libc_reallocarray, reallocarray)
diff --git a/malloc/scratch_buffer_grow.c b/malloc/scratch_buffer_grow.c
index 4714754897..bb45323176 100644
--- a/malloc/scratch_buffer_grow.c
+++ b/malloc/scratch_buffer_grow.c
@@ -1,5 +1,5 @@
/* Variable-sized buffer with on-stack default allocation.
- Copyright (C) 2015-2016 Free Software Foundation, Inc.
+ Copyright (C) 2015-2018 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,6 +16,10 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
+#ifndef _LIBC
+# include <libc-config.h>
+#endif
+
#include <scratch_buffer.h>
#include <errno.h>
@@ -49,4 +53,4 @@ __libc_scratch_buffer_grow (struct scratch_buffer *buffer)
buffer->length = new_length;
return true;
}
-libc_hidden_def (__libc_scratch_buffer_grow);
+libc_hidden_def (__libc_scratch_buffer_grow)
diff --git a/malloc/scratch_buffer_grow_preserve.c b/malloc/scratch_buffer_grow_preserve.c
index 6aae41e140..288ce9067d 100644
--- a/malloc/scratch_buffer_grow_preserve.c
+++ b/malloc/scratch_buffer_grow_preserve.c
@@ -1,5 +1,5 @@
/* Variable-sized buffer with on-stack default allocation.
- Copyright (C) 2015-2016 Free Software Foundation, Inc.
+ Copyright (C) 2015-2018 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,6 +16,10 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
+#ifndef _LIBC
+# include <libc-config.h>
+#endif
+
#include <scratch_buffer.h>
#include <errno.h>
#include <string.h>
@@ -26,14 +30,14 @@ __libc_scratch_buffer_grow_preserve (struct scratch_buffer *buffer)
size_t new_length = 2 * buffer->length;
void *new_ptr;
- if (buffer->data == buffer->__space)
+ if (buffer->data == buffer->__space.__c)
{
/* Move buffer to the heap. No overflow is possible because
buffer->length describes a small buffer on the stack. */
new_ptr = malloc (new_length);
if (new_ptr == NULL)
return false;
- memcpy (new_ptr, buffer->__space, buffer->length);
+ memcpy (new_ptr, buffer->__space.__c, buffer->length);
}
else
{
@@ -60,4 +64,4 @@ __libc_scratch_buffer_grow_preserve (struct scratch_buffer *buffer)
buffer->length = new_length;
return true;
}
-libc_hidden_def (__libc_scratch_buffer_grow_preserve);
+libc_hidden_def (__libc_scratch_buffer_grow_preserve)
diff --git a/malloc/scratch_buffer_set_array_size.c b/malloc/scratch_buffer_set_array_size.c
index 3d33731abb..1f4a203b61 100644
--- a/malloc/scratch_buffer_set_array_size.c
+++ b/malloc/scratch_buffer_set_array_size.c
@@ -1,5 +1,5 @@
/* Variable-sized buffer with on-stack default allocation.
- Copyright (C) 2015-2016 Free Software Foundation, Inc.
+ Copyright (C) 2015-2018 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,8 +16,13 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
+#ifndef _LIBC
+# include <libc-config.h>
+#endif
+
#include <scratch_buffer.h>
#include <errno.h>
+#include <limits.h>
bool
__libc_scratch_buffer_set_array_size (struct scratch_buffer *buffer,
@@ -56,4 +61,4 @@ __libc_scratch_buffer_set_array_size (struct scratch_buffer *buffer,
buffer->length = new_length;
return true;
}
-libc_hidden_def (__libc_scratch_buffer_set_array_size);
+libc_hidden_def (__libc_scratch_buffer_set_array_size)
diff --git a/malloc/set-freeres.c b/malloc/set-freeres.c
index f614fd75a7..cda368479f 100644
--- a/malloc/set-freeres.c
+++ b/malloc/set-freeres.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1997-2016 Free Software Foundation, Inc.
+/* Copyright (C) 1997-2018 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
@@ -26,6 +26,10 @@ DEFINE_HOOK (__libc_subfreeres, (void));
symbol_set_define (__libc_freeres_ptrs);
+extern __attribute__ ((weak)) void __libdl_freeres (void);
+
+extern __attribute__ ((weak)) void __libpthread_freeres (void);
+
void __libc_freeres_fn_section
__libc_freeres (void)
{
@@ -39,8 +43,19 @@ __libc_freeres (void)
_IO_cleanup ();
+ /* We run the resource freeing after IO cleanup. */
RUN_HOOK (__libc_subfreeres, ());
+ /* Call the libdl list of cleanup functions
+ (weak-ref-and-check). */
+ if (&__libdl_freeres != NULL)
+ __libdl_freeres ();
+
+ /* Call the libpthread list of cleanup functions
+ (weak-ref-and-check). */
+ if (&__libpthread_freeres != NULL)
+ __libpthread_freeres ();
+
for (p = symbol_set_first_element (__libc_freeres_ptrs);
!symbol_set_end_p (__libc_freeres_ptrs, p); ++p)
free (*p);
diff --git a/malloc/thread-freeres.c b/malloc/thread-freeres.c
index a8b5305a3e..a63b6c93f3 100644
--- a/malloc/thread-freeres.c
+++ b/malloc/thread-freeres.c
@@ -1,5 +1,5 @@
/* Free resources stored in thread-local variables on thread exit.
- Copyright (C) 2003-2016 Free Software Foundation, Inc.
+ Copyright (C) 2003-2018 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,24 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
-#include <stdlib.h>
#include <libc-internal.h>
-#include <set-hooks.h>
+#include <malloc-internal.h>
+#include <resolv/resolv-internal.h>
+#include <rpc/rpc.h>
+#include <string.h>
-#ifdef _LIBC_REENTRANT
-DEFINE_HOOK (__libc_thread_subfreeres, (void));
-
-void __attribute__ ((section ("__libc_thread_freeres_fn")))
+/* Thread shutdown function. Note that this function must be called
+ for threads during shutdown for correctness reasons. Unlike
+ __libc_subfreeres, skipping calls to it is not a valid optimization.
+ This is called directly from pthread_create as the thread exits. */
+void
__libc_thread_freeres (void)
{
- RUN_HOOK (__libc_thread_subfreeres, ());
+ call_function_static_weak (__rpc_thread_destroy);
+ call_function_static_weak (__res_thread_freeres);
+ call_function_static_weak (__strerror_thread_freeres);
+
+ /* This should come last because it shuts down malloc for this
+ thread and the other shutdown functions might well call free. */
+ call_function_static_weak (__malloc_arena_thread_freeres);
}
-#endif
diff --git a/malloc/tst-alloc_buffer.c b/malloc/tst-alloc_buffer.c
new file mode 100644
index 0000000000..1a2d5636b0
--- /dev/null
+++ b/malloc/tst-alloc_buffer.c
@@ -0,0 +1,665 @@
+/* Tests for struct alloc_buffer.
+ Copyright (C) 2017-2018 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/>. */
+
+#include <arpa/inet.h>
+#include <alloc_buffer.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/test-driver.h>
+
+/* Return true if PTR is sufficiently aligned for TYPE. */
+#define IS_ALIGNED(ptr, type) \
+ ((((uintptr_t) ptr) & (__alloc_buffer_assert_align (__alignof (type)) - 1)) \
+ == 0)
+
+/* Structure with non-power-of-two size. */
+struct twelve
+{
+ uint32_t buffer[3] __attribute__ ((aligned (4)));
+};
+_Static_assert (sizeof (struct twelve) == 12, "struct twelve");
+_Static_assert (__alignof__ (struct twelve) == 4, "struct twelve");
+
+/* Check for success obtaining empty arrays. Does not assume the
+ buffer is empty. */
+static void
+test_empty_array (struct alloc_buffer refbuf)
+{
+ bool refbuf_failed = alloc_buffer_has_failed (&refbuf);
+ if (test_verbose)
+ printf ("info: %s: current=0x%llx end=0x%llx refbuf_failed=%d\n",
+ __func__, (unsigned long long) refbuf.__alloc_buffer_current,
+ (unsigned long long) refbuf.__alloc_buffer_end, refbuf_failed);
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY ((alloc_buffer_alloc_bytes (&buf, 0) == NULL)
+ == refbuf_failed);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf) == refbuf_failed);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY ((alloc_buffer_alloc_array (&buf, char, 0) == NULL)
+ == refbuf_failed);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf) == refbuf_failed);
+ }
+ /* The following tests can fail due to the need for aligning the
+ returned pointer. */
+ {
+ struct alloc_buffer buf = refbuf;
+ bool expect_failure = refbuf_failed
+ || !IS_ALIGNED (alloc_buffer_next (&buf, void), double);
+ double *ptr = alloc_buffer_alloc_array (&buf, double, 0);
+ TEST_VERIFY (IS_ALIGNED (ptr, double));
+ TEST_VERIFY ((ptr == NULL) == expect_failure);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf) == expect_failure);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ bool expect_failure = refbuf_failed
+ || !IS_ALIGNED (alloc_buffer_next (&buf, void), struct twelve);
+ struct twelve *ptr = alloc_buffer_alloc_array (&buf, struct twelve, 0);
+ TEST_VERIFY (IS_ALIGNED (ptr, struct twelve));
+ TEST_VERIFY ((ptr == NULL) == expect_failure);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf) == expect_failure);
+ }
+}
+
+/* Test allocation of impossibly large arrays. */
+static void
+test_impossible_array (struct alloc_buffer refbuf)
+{
+ if (test_verbose)
+ printf ("info: %s: current=0x%llx end=0x%llx\n",
+ __func__, (unsigned long long) refbuf.__alloc_buffer_current,
+ (unsigned long long) refbuf.__alloc_buffer_end);
+ static const size_t counts[] =
+ { SIZE_MAX, SIZE_MAX - 1, SIZE_MAX - 2, SIZE_MAX - 3, SIZE_MAX - 4,
+ SIZE_MAX / 2, SIZE_MAX / 2 + 1, SIZE_MAX / 2 - 1, 0};
+
+ for (int i = 0; counts[i] != 0; ++i)
+ {
+ size_t count = counts[i];
+ if (test_verbose)
+ printf ("info: %s: count=%zu\n", __func__, count);
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_bytes (&buf, count) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, count) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, short, count) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, count) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, count)
+ == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ }
+}
+
+/* Check for failure to obtain anything from a failed buffer. */
+static void
+test_after_failure (struct alloc_buffer refbuf)
+{
+ if (test_verbose)
+ printf ("info: %s: current=0x%llx end=0x%llx\n",
+ __func__, (unsigned long long) refbuf.__alloc_buffer_current,
+ (unsigned long long) refbuf.__alloc_buffer_end);
+ TEST_VERIFY (alloc_buffer_has_failed (&refbuf));
+ {
+ struct alloc_buffer buf = refbuf;
+ alloc_buffer_add_byte (&buf, 17);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, char) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, double) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, struct twelve) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+
+ test_impossible_array (refbuf);
+ for (int count = 0; count <= 4; ++count)
+ {
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_bytes (&buf, count) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, count) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, count) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, count)
+ == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+ }
+}
+
+static void
+test_empty (struct alloc_buffer refbuf)
+{
+ TEST_VERIFY (alloc_buffer_size (&refbuf) == 0);
+ if (alloc_buffer_next (&refbuf, void) != NULL)
+ TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
+ test_empty_array (refbuf);
+ test_impossible_array (refbuf);
+
+ /* Failure to obtain non-empty objects. */
+ {
+ struct alloc_buffer buf = refbuf;
+ alloc_buffer_add_byte (&buf, 17);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, char) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, double) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, struct twelve) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, 1) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, 1) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, 1) == NULL);
+ test_after_failure (buf);
+ }
+}
+
+static void
+test_size_1 (struct alloc_buffer refbuf)
+{
+ TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
+ TEST_VERIFY (alloc_buffer_size (&refbuf) == 1);
+ test_empty_array (refbuf);
+ test_impossible_array (refbuf);
+
+ /* Success adding a single byte. */
+ {
+ struct alloc_buffer buf = refbuf;
+ alloc_buffer_add_byte (&buf, 17);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ test_empty (buf);
+ }
+ TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\x11", 1) == 0);
+ {
+ struct alloc_buffer buf = refbuf;
+ signed char *ptr = alloc_buffer_alloc (&buf, signed char);
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ *ptr = 126;
+ test_empty (buf);
+ }
+ TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\176", 1) == 0);
+ {
+ struct alloc_buffer buf = refbuf;
+ char *ptr = alloc_buffer_alloc_array (&buf, char, 1);
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ *ptr = (char) 253;
+ test_empty (buf);
+ }
+ TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\xfd", 1) == 0);
+
+ /* Failure with larger objects. */
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, short) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, double) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, struct twelve) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, short, 1) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, double, 1) == NULL);
+ test_after_failure (buf);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct twelve, 1) == NULL);
+ test_after_failure (buf);
+ }
+}
+
+static void
+test_size_2 (struct alloc_buffer refbuf)
+{
+ TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
+ TEST_VERIFY (alloc_buffer_size (&refbuf) == 2);
+ TEST_VERIFY (IS_ALIGNED (alloc_buffer_next (&refbuf, void), short));
+ test_empty_array (refbuf);
+ test_impossible_array (refbuf);
+
+ /* Success adding two bytes. */
+ {
+ struct alloc_buffer buf = refbuf;
+ alloc_buffer_add_byte (&buf, '@');
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ test_size_1 (buf);
+ }
+ TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "@\xfd", 2) == 0);
+ {
+ struct alloc_buffer buf = refbuf;
+ signed char *ptr = alloc_buffer_alloc (&buf, signed char);
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ *ptr = 'A';
+ test_size_1 (buf);
+ }
+ TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "A\xfd", 2) == 0);
+ {
+ struct alloc_buffer buf = refbuf;
+ char *ptr = alloc_buffer_alloc_array (&buf, char, 1);
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ *ptr = 'B';
+ test_size_1 (buf);
+ }
+ TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "B\xfd", 2) == 0);
+ {
+ struct alloc_buffer buf = refbuf;
+ unsigned short *ptr = alloc_buffer_alloc (&buf, unsigned short);
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (IS_ALIGNED (ptr, unsigned short));
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ *ptr = htons (0x12f4);
+ test_empty (buf);
+ }
+ TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\x12\xf4", 2) == 0);
+ {
+ struct alloc_buffer buf = refbuf;
+ unsigned short *ptr = alloc_buffer_alloc_array (&buf, unsigned short, 1);
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (IS_ALIGNED (ptr, unsigned short));
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ *ptr = htons (0x13f5);
+ test_empty (buf);
+ }
+ TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "\x13\xf5", 2) == 0);
+ {
+ struct alloc_buffer buf = refbuf;
+ char *ptr = alloc_buffer_alloc_array (&buf, char, 2);
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ memcpy (ptr, "12", 2);
+ test_empty (buf);
+ }
+ TEST_VERIFY (memcmp (alloc_buffer_next (&refbuf, void), "12", 2) == 0);
+}
+
+static void
+test_misaligned (char pad)
+{
+ enum { SIZE = 23 };
+ char *backing = xmalloc (SIZE + 2);
+ backing[0] = ~pad;
+ backing[SIZE + 1] = pad;
+ struct alloc_buffer refbuf = alloc_buffer_create (backing + 1, SIZE);
+
+ {
+ struct alloc_buffer buf = refbuf;
+ short *ptr = alloc_buffer_alloc_array (&buf, short, SIZE / sizeof (short));
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (IS_ALIGNED (ptr, short));
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ for (int i = 0; i < SIZE / sizeof (short); ++i)
+ ptr[i] = htons (0xff01 + i);
+ TEST_VERIFY (memcmp (ptr,
+ "\xff\x01\xff\x02\xff\x03\xff\x04"
+ "\xff\x05\xff\x06\xff\x07\xff\x08"
+ "\xff\x09\xff\x0a\xff\x0b", 22) == 0);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ uint32_t *ptr = alloc_buffer_alloc_array
+ (&buf, uint32_t, SIZE / sizeof (uint32_t));
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (IS_ALIGNED (ptr, uint32_t));
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ for (int i = 0; i < SIZE / sizeof (uint32_t); ++i)
+ ptr[i] = htonl (0xf1e2d301 + i);
+ TEST_VERIFY (memcmp (ptr,
+ "\xf1\xe2\xd3\x01\xf1\xe2\xd3\x02"
+ "\xf1\xe2\xd3\x03\xf1\xe2\xd3\x04"
+ "\xf1\xe2\xd3\x05", 20) == 0);
+ }
+ {
+ struct alloc_buffer buf = refbuf;
+ struct twelve *ptr = alloc_buffer_alloc (&buf, struct twelve);
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (IS_ALIGNED (ptr, struct twelve));
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ ptr->buffer[0] = htonl (0x11223344);
+ ptr->buffer[1] = htonl (0x55667788);
+ ptr->buffer[2] = htonl (0x99aabbcc);
+ TEST_VERIFY (memcmp (ptr,
+ "\x11\x22\x33\x44"
+ "\x55\x66\x77\x88"
+ "\x99\xaa\xbb\xcc", 12) == 0);
+ }
+ {
+ static const double nums[] = { 1, 2 };
+ struct alloc_buffer buf = refbuf;
+ double *ptr = alloc_buffer_alloc_array (&buf, double, 2);
+ TEST_VERIFY_EXIT (ptr != NULL);
+ TEST_VERIFY (IS_ALIGNED (ptr, double));
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ ptr[0] = nums[0];
+ ptr[1] = nums[1];
+ TEST_VERIFY (memcmp (ptr, nums, sizeof (nums)) == 0);
+ }
+
+ /* Verify that padding was not overwritten. */
+ TEST_VERIFY (backing[0] == (char) ~pad);
+ TEST_VERIFY (backing[SIZE + 1] == pad);
+ free (backing);
+}
+
+/* Check that overflow during alignment is handled properly. */
+static void
+test_large_misaligned (void)
+{
+ uintptr_t minus1 = -1;
+ uintptr_t start = minus1 & ~0xfe;
+ struct alloc_buffer refbuf = alloc_buffer_create ((void *) start, 16);
+ TEST_VERIFY (!alloc_buffer_has_failed (&refbuf));
+
+ struct __attribute__ ((aligned (256))) align256
+ {
+ int dymmy;
+ };
+
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc (&buf, struct align256) == NULL);
+ test_after_failure (buf);
+ }
+ for (int count = 0; count < 3; ++count)
+ {
+ struct alloc_buffer buf = refbuf;
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, struct align256, count)
+ == NULL);
+ test_after_failure (buf);
+ }
+}
+
+/* Check behavior of large allocations. */
+static void
+test_large (void)
+{
+ {
+ /* Allocation which wraps around. */
+ struct alloc_buffer buf = { 1, SIZE_MAX };
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, char, SIZE_MAX) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+
+ {
+ /* Successful very large allocation. */
+ struct alloc_buffer buf = { 1, SIZE_MAX };
+ uintptr_t val = (uintptr_t) alloc_buffer_alloc_array
+ (&buf, char, SIZE_MAX - 1);
+ TEST_VERIFY (val == 1);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ test_empty (buf);
+ }
+
+ {
+ typedef char __attribute__ ((aligned (2))) char2;
+
+ /* Overflow in array size computation. */
+ struct alloc_buffer buf = { 1, SIZE_MAX };
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, char2, SIZE_MAX - 1) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+
+ /* Successful allocation after alignment. */
+ buf = (struct alloc_buffer) { 1, SIZE_MAX };
+ uintptr_t val = (uintptr_t) alloc_buffer_alloc_array
+ (&buf, char2, SIZE_MAX - 2);
+ TEST_VERIFY (val == 2);
+ test_empty (buf);
+
+ /* Alignment behavior near the top of the address space. */
+ buf = (struct alloc_buffer) { SIZE_MAX, SIZE_MAX };
+ TEST_VERIFY (alloc_buffer_next (&buf, char2) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ buf = (struct alloc_buffer) { SIZE_MAX, SIZE_MAX };
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, char2, 0) == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ }
+
+ {
+ typedef short __attribute__ ((aligned (2))) short2;
+
+ /* Test overflow in size computation. */
+ struct alloc_buffer buf = { 1, SIZE_MAX };
+ TEST_VERIFY (alloc_buffer_alloc_array (&buf, short2, SIZE_MAX / 2)
+ == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+
+ /* A slightly smaller array fits within the allocation. */
+ buf = (struct alloc_buffer) { 2, SIZE_MAX - 1 };
+ uintptr_t val = (uintptr_t) alloc_buffer_alloc_array
+ (&buf, short2, SIZE_MAX / 2 - 1);
+ TEST_VERIFY (val == 2);
+ test_empty (buf);
+ }
+}
+
+static void
+test_copy_bytes (void)
+{
+ char backing[4];
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ alloc_buffer_copy_bytes (&buf, "1", 1);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (alloc_buffer_size (&buf) == 3);
+ TEST_VERIFY (memcmp (backing, "1@@@", 4) == 0);
+ }
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ alloc_buffer_copy_bytes (&buf, "12", 3);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (alloc_buffer_size (&buf) == 1);
+ TEST_VERIFY (memcmp (backing, "12\0@", 4) == 0);
+ }
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ alloc_buffer_copy_bytes (&buf, "1234", 4);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (alloc_buffer_size (&buf) == 0);
+ TEST_VERIFY (memcmp (backing, "1234", 4) == 0);
+ }
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ alloc_buffer_copy_bytes (&buf, "1234", 5);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
+ }
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ alloc_buffer_copy_bytes (&buf, "1234", -1);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
+ }
+}
+
+static void
+test_copy_string (void)
+{
+ char backing[4];
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ const char *p = alloc_buffer_copy_string (&buf, "");
+ TEST_VERIFY (p == backing);
+ TEST_VERIFY (strcmp (p, "") == 0);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (alloc_buffer_size (&buf) == 3);
+ TEST_VERIFY (memcmp (backing, "\0@@@", 4) == 0);
+ }
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ const char *p = alloc_buffer_copy_string (&buf, "1");
+ TEST_VERIFY (p == backing);
+ TEST_VERIFY (strcmp (p, "1") == 0);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (alloc_buffer_size (&buf) == 2);
+ TEST_VERIFY (memcmp (backing, "1\0@@", 4) == 0);
+ }
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ const char *p = alloc_buffer_copy_string (&buf, "12");
+ TEST_VERIFY (p == backing);
+ TEST_VERIFY (strcmp (p, "12") == 0);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (alloc_buffer_size (&buf) == 1);
+ TEST_VERIFY (memcmp (backing, "12\0@", 4) == 0);
+ }
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ const char *p = alloc_buffer_copy_string (&buf, "123");
+ TEST_VERIFY (p == backing);
+ TEST_VERIFY (strcmp (p, "123") == 0);
+ TEST_VERIFY (!alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (alloc_buffer_size (&buf) == 0);
+ TEST_VERIFY (memcmp (backing, "123", 4) == 0);
+ }
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ TEST_VERIFY (alloc_buffer_copy_string (&buf, "1234") == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
+ }
+ {
+ memset (backing, '@', sizeof (backing));
+ struct alloc_buffer buf = alloc_buffer_create (backing, sizeof (backing));
+ TEST_VERIFY (alloc_buffer_copy_string (&buf, "12345") == NULL);
+ TEST_VERIFY (alloc_buffer_has_failed (&buf));
+ TEST_VERIFY (memcmp (backing, "@@@@", 4) == 0);
+ }
+}
+
+static int
+do_test (void)
+{
+ test_empty (alloc_buffer_create (NULL, 0));
+ test_empty (alloc_buffer_create ((char *) "", 0));
+ test_empty (alloc_buffer_create ((void *) 1, 0));
+
+ {
+ void *ptr = (void *) ""; /* Cannot be freed. */
+ struct alloc_buffer buf = alloc_buffer_allocate (1, &ptr);
+ test_size_1 (buf);
+ free (ptr); /* Should have been overwritten. */
+ }
+
+ {
+ void *ptr= (void *) ""; /* Cannot be freed. */
+ struct alloc_buffer buf = alloc_buffer_allocate (2, &ptr);
+ test_size_2 (buf);
+ free (ptr); /* Should have been overwritten. */
+ }
+
+ test_misaligned (0);
+ test_misaligned (0xc7);
+ test_misaligned (0xff);
+
+ test_large_misaligned ();
+ test_large ();
+ test_copy_bytes ();
+ test_copy_string ();
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/malloc/tst-calloc.c b/malloc/tst-calloc.c
index 9a879f4cfe..f014ea223c 100644
--- a/malloc/tst-calloc.c
+++ b/malloc/tst-calloc.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2000-2016 Free Software Foundation, Inc.
+/* Copyright (C) 2000-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@redhat.com>.
diff --git a/malloc/tst-dynarray-at-fail.c b/malloc/tst-dynarray-at-fail.c
new file mode 100644
index 0000000000..d693c2358e
--- /dev/null
+++ b/malloc/tst-dynarray-at-fail.c
@@ -0,0 +1,125 @@
+/* Test reporting of out-of-bounds access for dynamic arrays.
+ Copyright (C) 2017-2018 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/>. */
+
+#include "tst-dynarray-shared.h"
+
+#include <signal.h>
+#include <stdint.h>
+#include <string.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+
+/* Run CALLBACK and check that the data on standard error equals
+ EXPECTED. */
+static void
+check (const char *test, void (*callback) (void *), size_t index,
+ const char *expected)
+{
+ struct support_capture_subprocess result
+ = support_capture_subprocess (callback, &index);
+ if (strcmp (result.err.buffer, expected) != 0)
+ {
+ support_record_failure ();
+ printf ("error: test %s (%zu) unexpected standard error data\n"
+ " expected: %s\n"
+ " actual: %s\n",
+ test, index, expected, result.err.buffer);
+ }
+ TEST_VERIFY (strlen (result.out.buffer) == 0);
+ TEST_VERIFY (WIFSIGNALED (result.status));
+ if (WIFSIGNALED (result.status))
+ TEST_VERIFY (WTERMSIG (result.status) == SIGABRT);
+ support_capture_subprocess_free (&result);
+}
+
+/* Try indexing an empty array. */
+static void
+test_empty (void *closure)
+{
+ size_t *pindex = closure;
+ struct dynarray_int dyn;
+ dynarray_int_init (&dyn);
+ dynarray_int_at (&dyn, *pindex);
+}
+
+/* Try indexing a one-element array. */
+static void
+test_one (void *closure)
+{
+ size_t *pindex = closure;
+ struct dynarray_int dyn;
+ dynarray_int_init (&dyn);
+ TEST_VERIFY (dynarray_int_resize (&dyn, 1));
+ dynarray_int_at (&dyn, *pindex);
+}
+
+/* Try indexing a longer array. */
+static void
+test_many (void *closure)
+{
+ size_t *pindex = closure;
+ struct dynarray_int dyn;
+ dynarray_int_init (&dyn);
+ TEST_VERIFY (dynarray_int_resize (&dyn, 5371));
+ dynarray_int_at (&dyn, *pindex);
+}
+
+/* (size_t) -1 for use in string literals. */
+#if SIZE_WIDTH == 32
+# define MINUS_1 "4294967295"
+#elif SIZE_WIDTH == 64
+# define MINUS_1 "18446744073709551615"
+#else
+# error "unknown value for SIZE_WIDTH"
+#endif
+
+static int
+do_test (void)
+{
+ TEST_VERIFY (setenv ("LIBC_FATAL_STDERR_", "1", 1) == 0);
+
+ check ("test_empty", test_empty, 0,
+ "Fatal glibc error: array index 0 not less than array length 0\n");
+ check ("test_empty", test_empty, 1,
+ "Fatal glibc error: array index 1 not less than array length 0\n");
+ check ("test_empty", test_empty, -1,
+ "Fatal glibc error: array index " MINUS_1
+ " not less than array length 0\n");
+
+ check ("test_one", test_one, 1,
+ "Fatal glibc error: array index 1 not less than array length 1\n");
+ check ("test_one", test_one, 2,
+ "Fatal glibc error: array index 2 not less than array length 1\n");
+ check ("test_one", test_one, -1,
+ "Fatal glibc error: array index " MINUS_1
+ " not less than array length 1\n");
+
+ check ("test_many", test_many, 5371,
+ "Fatal glibc error: array index 5371"
+ " not less than array length 5371\n");
+ check ("test_many", test_many, 5372,
+ "Fatal glibc error: array index 5372"
+ " not less than array length 5371\n");
+ check ("test_many", test_many, -1,
+ "Fatal glibc error: array index " MINUS_1
+ " not less than array length 5371\n");
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/malloc/tst-dynarray-fail.c b/malloc/tst-dynarray-fail.c
new file mode 100644
index 0000000000..1190524e2d
--- /dev/null
+++ b/malloc/tst-dynarray-fail.c
@@ -0,0 +1,418 @@
+/* Test allocation failures with dynamic arrays.
+ Copyright (C) 2017-2018 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/>. */
+
+/* This test is separate from tst-dynarray because it cannot run under
+ valgrind. */
+
+#include "tst-dynarray-shared.h"
+
+#include <mcheck.h>
+#include <stdio.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xunistd.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+/* Data structure to fill up the heap. */
+struct heap_filler
+{
+ struct heap_filler *next;
+};
+
+/* Allocate objects until the heap is full. */
+static struct heap_filler *
+fill_heap (void)
+{
+ size_t pad = 4096;
+ struct heap_filler *head = NULL;
+ while (true)
+ {
+ struct heap_filler *new_head = malloc (sizeof (*new_head) + pad);
+ if (new_head == NULL)
+ {
+ if (pad > 0)
+ {
+ /* Try again with smaller allocations. */
+ pad = 0;
+ continue;
+ }
+ else
+ break;
+ }
+ new_head->next = head;
+ head = new_head;
+ }
+ return head;
+}
+
+/* Free the heap-filling allocations, so that we can continue testing
+ and detect memory leaks elsewhere. */
+static void
+free_fill_heap (struct heap_filler *head)
+{
+ while (head != NULL)
+ {
+ struct heap_filler *next = head->next;
+ free (head);
+ head = next;
+ }
+}
+
+/* Check allocation failures for int arrays (without an element free
+ function). */
+static void
+test_int_fail (void)
+{
+ /* Exercise failure in add/emplace.
+
+ do_add: Use emplace (false) or add (true) to add elements.
+ do_finalize: Perform finalization at the end (instead of free). */
+ for (int do_add = 0; do_add < 2; ++do_add)
+ for (int do_finalize = 0; do_finalize < 2; ++do_finalize)
+ {
+ struct dynarray_int dyn;
+ dynarray_int_init (&dyn);
+ size_t count = 0;
+ while (true)
+ {
+ if (do_add)
+ {
+ dynarray_int_add (&dyn, 0);
+ if (dynarray_int_has_failed (&dyn))
+ break;
+ }
+ else
+ {
+ int *place = dynarray_int_emplace (&dyn);
+ if (place == NULL)
+ break;
+ TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
+ *place = 0;
+ }
+ ++count;
+ }
+ printf ("info: %s: failure after %zu elements\n", __func__, count);
+ TEST_VERIFY_EXIT (dynarray_int_has_failed (&dyn));
+ if (do_finalize)
+ {
+ struct int_array result = { (int *) (uintptr_t) -1, -1 };
+ TEST_VERIFY_EXIT (!dynarray_int_finalize (&dyn, &result));
+ TEST_VERIFY_EXIT (result.array == (int *) (uintptr_t) -1);
+ TEST_VERIFY_EXIT (result.length == (size_t) -1);
+ }
+ else
+ dynarray_int_free (&dyn);
+ CHECK_INIT_STATE (int, &dyn);
+ }
+
+ /* Exercise failure in finalize. */
+ for (int do_add = 0; do_add < 2; ++do_add)
+ {
+ struct dynarray_int dyn;
+ dynarray_int_init (&dyn);
+ for (unsigned int i = 0; i < 10000; ++i)
+ {
+ if (do_add)
+ {
+ dynarray_int_add (&dyn, i);
+ TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
+ }
+ else
+ {
+ int *place = dynarray_int_emplace (&dyn);
+ TEST_VERIFY_EXIT (place != NULL);
+ *place = i;
+ }
+ }
+ TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
+ struct heap_filler *heap_filler = fill_heap ();
+ struct int_array result = { (int *) (uintptr_t) -1, -1 };
+ TEST_VERIFY_EXIT (!dynarray_int_finalize (&dyn, &result));
+ TEST_VERIFY_EXIT (result.array == (int *) (uintptr_t) -1);
+ TEST_VERIFY_EXIT (result.length == (size_t) -1);
+ CHECK_INIT_STATE (int, &dyn);
+ free_fill_heap (heap_filler);
+ }
+
+ /* Exercise failure in resize. */
+ {
+ struct dynarray_int dyn;
+ dynarray_int_init (&dyn);
+ struct heap_filler *heap_filler = fill_heap ();
+ TEST_VERIFY (!dynarray_int_resize (&dyn, 1000));
+ TEST_VERIFY (dynarray_int_has_failed (&dyn));
+ free_fill_heap (heap_filler);
+
+ dynarray_int_init (&dyn);
+ TEST_VERIFY (dynarray_int_resize (&dyn, 1));
+ heap_filler = fill_heap ();
+ TEST_VERIFY (!dynarray_int_resize (&dyn, 1000));
+ TEST_VERIFY (dynarray_int_has_failed (&dyn));
+ free_fill_heap (heap_filler);
+
+ dynarray_int_init (&dyn);
+ TEST_VERIFY (dynarray_int_resize (&dyn, 1000));
+ heap_filler = fill_heap ();
+ TEST_VERIFY (!dynarray_int_resize (&dyn, 2000));
+ TEST_VERIFY (dynarray_int_has_failed (&dyn));
+ free_fill_heap (heap_filler);
+ }
+}
+
+/* Check allocation failures for char * arrays (which automatically
+ free the pointed-to strings). */
+static void
+test_str_fail (void)
+{
+ /* Exercise failure in add/emplace.
+
+ do_add: Use emplace (false) or add (true) to add elements.
+ do_finalize: Perform finalization at the end (instead of free). */
+ for (int do_add = 0; do_add < 2; ++do_add)
+ for (int do_finalize = 0; do_finalize < 2; ++do_finalize)
+ {
+ struct dynarray_str dyn;
+ dynarray_str_init (&dyn);
+ size_t count = 0;
+ while (true)
+ {
+ char **place;
+ if (do_add)
+ {
+ dynarray_str_add (&dyn, NULL);
+ if (dynarray_str_has_failed (&dyn))
+ break;
+ else
+ place = dynarray_str_at (&dyn, dynarray_str_size (&dyn) - 1);
+ }
+ else
+ {
+ place = dynarray_str_emplace (&dyn);
+ if (place == NULL)
+ break;
+ }
+ TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
+ TEST_VERIFY_EXIT (*place == NULL);
+ *place = strdup ("placeholder");
+ if (*place == NULL)
+ {
+ /* Second loop to wait for failure of
+ dynarray_str_emplace. */
+ while (true)
+ {
+ if (do_add)
+ {
+ dynarray_str_add (&dyn, NULL);
+ if (dynarray_str_has_failed (&dyn))
+ break;
+ }
+ else
+ {
+ char **place = dynarray_str_emplace (&dyn);
+ if (place == NULL)
+ break;
+ TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
+ *place = NULL;
+ }
+ ++count;
+ }
+ break;
+ }
+ ++count;
+ }
+ printf ("info: %s: failure after %zu elements\n", __func__, count);
+ TEST_VERIFY_EXIT (dynarray_str_has_failed (&dyn));
+ if (do_finalize)
+ {
+ struct str_array result = { (char **) (uintptr_t) -1, -1 };
+ TEST_VERIFY_EXIT (!dynarray_str_finalize (&dyn, &result));
+ TEST_VERIFY_EXIT (result.array == (char **) (uintptr_t) -1);
+ TEST_VERIFY_EXIT (result.length == (size_t) -1);
+ }
+ else
+ dynarray_str_free (&dyn);
+ TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
+ TEST_VERIFY_EXIT (dyn.dynarray_header.array == dyn.scratch);
+ TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == 0);
+ TEST_VERIFY_EXIT (dyn.dynarray_header.allocated > 0);
+ }
+
+ /* Exercise failure in finalize. */
+ for (int do_add = 0; do_add < 2; ++do_add)
+ {
+ struct dynarray_str dyn;
+ dynarray_str_init (&dyn);
+ for (unsigned int i = 0; i < 1000; ++i)
+ {
+ if (do_add)
+ dynarray_str_add (&dyn, xstrdup ("placeholder"));
+ else
+ {
+ char **place = dynarray_str_emplace (&dyn);
+ TEST_VERIFY_EXIT (place != NULL);
+ TEST_VERIFY_EXIT (*place == NULL);
+ *place = xstrdup ("placeholder");
+ }
+ }
+ TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
+ struct heap_filler *heap_filler = fill_heap ();
+ struct str_array result = { (char **) (uintptr_t) -1, -1 };
+ TEST_VERIFY_EXIT (!dynarray_str_finalize (&dyn, &result));
+ TEST_VERIFY_EXIT (result.array == (char **) (uintptr_t) -1);
+ TEST_VERIFY_EXIT (result.length == (size_t) -1);
+ TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
+ TEST_VERIFY_EXIT (dyn.dynarray_header.array == dyn.scratch);
+ TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == 0);
+ TEST_VERIFY_EXIT (dyn.dynarray_header.allocated > 0);
+ free_fill_heap (heap_filler);
+ }
+
+ /* Exercise failure in resize. */
+ {
+ struct dynarray_str dyn;
+ dynarray_str_init (&dyn);
+ struct heap_filler *heap_filler = fill_heap ();
+ TEST_VERIFY (!dynarray_str_resize (&dyn, 1000));
+ TEST_VERIFY (dynarray_str_has_failed (&dyn));
+ free_fill_heap (heap_filler);
+
+ dynarray_str_init (&dyn);
+ TEST_VERIFY (dynarray_str_resize (&dyn, 1));
+ *dynarray_str_at (&dyn, 0) = xstrdup ("allocated");
+ heap_filler = fill_heap ();
+ TEST_VERIFY (!dynarray_str_resize (&dyn, 1000));
+ TEST_VERIFY (dynarray_str_has_failed (&dyn));
+ free_fill_heap (heap_filler);
+
+ dynarray_str_init (&dyn);
+ TEST_VERIFY (dynarray_str_resize (&dyn, 1000));
+ *dynarray_str_at (&dyn, 0) = xstrdup ("allocated");
+ heap_filler = fill_heap ();
+ TEST_VERIFY (!dynarray_str_resize (&dyn, 2000));
+ TEST_VERIFY (dynarray_str_has_failed (&dyn));
+ free_fill_heap (heap_filler);
+ }
+}
+
+/* Test if mmap can allocate a page. This is necessary because
+ setrlimit does not fail even if it reduces the RLIMIT_AS limit
+ below what is currently needed by the process. */
+static bool
+mmap_works (void)
+{
+ void *ptr = mmap (NULL, 1, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ if (ptr == MAP_FAILED)
+ return false;
+ xmunmap (ptr, 1);
+ return true;
+}
+
+/* Set the RLIMIT_AS limit to the value in *LIMIT. */
+static void
+xsetrlimit_as (const struct rlimit *limit)
+{
+ if (setrlimit (RLIMIT_AS, limit) != 0)
+ FAIL_EXIT1 ("setrlimit (RLIMIT_AS, %lu): %m",
+ (unsigned long) limit->rlim_cur);
+}
+
+/* Approximately this many bytes can be allocated after
+ reduce_rlimit_as has run. */
+enum { as_limit_reserve = 2 * 1024 * 1024 };
+
+/* Limit the size of the process, so that memory allocation in
+ allocate_thread will eventually fail, without impacting the entire
+ system. By default, a dynamic limit which leaves room for 2 MiB is
+ activated. The TEST_RLIMIT_AS environment variable overrides
+ it. */
+static void
+reduce_rlimit_as (void)
+{
+ struct rlimit limit;
+ if (getrlimit (RLIMIT_AS, &limit) != 0)
+ FAIL_EXIT1 ("getrlimit (RLIMIT_AS) failed: %m");
+
+ /* Use the TEST_RLIMIT_AS setting if available. */
+ {
+ long target = 0;
+ const char *variable = "TEST_RLIMIT_AS";
+ const char *target_str = getenv (variable);
+ if (target_str != NULL)
+ {
+ target = atoi (target_str);
+ if (target <= 0)
+ FAIL_EXIT1 ("invalid %s value: \"%s\"", variable, target_str);
+ printf ("info: setting RLIMIT_AS to %ld MiB\n", target);
+ target *= 1024 * 1024; /* Convert to megabytes. */
+ limit.rlim_cur = target;
+ xsetrlimit_as (&limit);
+ return;
+ }
+ }
+
+ /* Otherwise, try to find the limit with a binary search. */
+ unsigned long low = 1 << 20;
+ limit.rlim_cur = low;
+ xsetrlimit_as (&limit);
+
+ /* Find working upper limit. */
+ unsigned long high = 1 << 30;
+ while (true)
+ {
+ limit.rlim_cur = high;
+ xsetrlimit_as (&limit);
+ if (mmap_works ())
+ break;
+ if (2 * high < high)
+ FAIL_EXIT1 ("cannot find upper AS limit");
+ high *= 2;
+ }
+
+ /* Perform binary search. */
+ while ((high - low) > 128 * 1024)
+ {
+ unsigned long middle = (low + high) / 2;
+ limit.rlim_cur = middle;
+ xsetrlimit_as (&limit);
+ if (mmap_works ())
+ high = middle;
+ else
+ low = middle;
+ }
+
+ unsigned long target = high + as_limit_reserve;
+ limit.rlim_cur = target;
+ xsetrlimit_as (&limit);
+ printf ("info: RLIMIT_AS limit: %lu bytes\n", target);
+}
+
+static int
+do_test (void)
+{
+ mtrace ();
+ reduce_rlimit_as ();
+ test_int_fail ();
+ test_str_fail ();
+ return 0;
+}
+
+#define TIMEOUT 90
+#include <support/test-driver.c>
diff --git a/malloc/tst-dynarray-shared.h b/malloc/tst-dynarray-shared.h
new file mode 100644
index 0000000000..f7315d012c
--- /dev/null
+++ b/malloc/tst-dynarray-shared.h
@@ -0,0 +1,80 @@
+/* Shared definitions for dynarray tests.
+ Copyright (C) 2017-2018 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/>. */
+
+#include <stddef.h>
+
+struct int_array
+{
+ int *array;
+ size_t length;
+};
+
+#define DYNARRAY_STRUCT dynarray_int
+#define DYNARRAY_ELEMENT int
+#define DYNARRAY_PREFIX dynarray_int_
+#define DYNARRAY_FINAL_TYPE struct int_array
+#include <malloc/dynarray-skeleton.c>
+
+struct str_array
+{
+ char **array;
+ size_t length;
+};
+
+#define DYNARRAY_STRUCT dynarray_str
+#define DYNARRAY_ELEMENT char *
+#define DYNARRAY_ELEMENT_FREE(ptr) free (*ptr)
+#define DYNARRAY_PREFIX dynarray_str_
+#define DYNARRAY_FINAL_TYPE struct str_array
+#include <malloc/dynarray-skeleton.c>
+
+/* Check that *DYN is equivalent to its initial state. */
+#define CHECK_INIT_STATE(type, dyn) \
+ ({ \
+ TEST_VERIFY_EXIT (!dynarray_##type##_has_failed (dyn)); \
+ TEST_VERIFY_EXIT (dynarray_##type##_size (dyn) == 0); \
+ TEST_VERIFY_EXIT ((dyn)->dynarray_header.array \
+ == (dyn)->scratch); \
+ TEST_VERIFY_EXIT ((dyn)->dynarray_header.allocated > 0); \
+ (void) 0; \
+ })
+
+/* Check that *DYN behaves as if it is in its initial state. */
+#define CHECK_EMPTY(type, dyn) \
+ ({ \
+ CHECK_INIT_STATE (type, (dyn)); \
+ dynarray_##type##_free (dyn); \
+ CHECK_INIT_STATE (type, (dyn)); \
+ dynarray_##type##_clear (dyn); \
+ CHECK_INIT_STATE (type, (dyn)); \
+ dynarray_##type##_remove_last (dyn); \
+ CHECK_INIT_STATE (type, (dyn)); \
+ dynarray_##type##_mark_failed (dyn); \
+ TEST_VERIFY_EXIT (dynarray_##type##_has_failed (dyn)); \
+ dynarray_##type##_clear (dyn); \
+ TEST_VERIFY_EXIT (dynarray_##type##_has_failed (dyn)); \
+ dynarray_##type##_remove_last (dyn); \
+ TEST_VERIFY_EXIT (dynarray_##type##_has_failed (dyn)); \
+ TEST_VERIFY_EXIT (dynarray_##type##_emplace (dyn) == NULL); \
+ dynarray_##type##_free (dyn); \
+ CHECK_INIT_STATE (type, (dyn)); \
+ /* These functions should not assert. */ \
+ dynarray_##type##_begin (dyn); \
+ dynarray_##type##_end (dyn); \
+ (void) 0; \
+ })
diff --git a/malloc/tst-dynarray.c b/malloc/tst-dynarray.c
new file mode 100644
index 0000000000..0a5716b0ec
--- /dev/null
+++ b/malloc/tst-dynarray.c
@@ -0,0 +1,574 @@
+/* Test for dynamic arrays.
+ Copyright (C) 2017-2018 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/>. */
+
+#include "tst-dynarray-shared.h"
+
+#include <errno.h>
+#include <stdint.h>
+
+#define DYNARRAY_STRUCT dynarray_long
+#define DYNARRAY_ELEMENT long
+#define DYNARRAY_PREFIX dynarray_long_
+#define DYNARRAY_ELEMENT_INIT(e) (*(e) = 17)
+#include <malloc/dynarray-skeleton.c>
+
+struct long_array
+{
+ long *array;
+ size_t length;
+};
+
+#define DYNARRAY_STRUCT dynarray_long_noscratch
+#define DYNARRAY_ELEMENT long
+#define DYNARRAY_PREFIX dynarray_long_noscratch_
+#define DYNARRAY_ELEMENT_INIT(e) (*(e) = 23)
+#define DYNARRAY_FINAL_TYPE struct long_array
+#define DYNARRAY_INITIAL_SIZE 0
+#include <malloc/dynarray-skeleton.c>
+
+#define DYNARRAY_STRUCT zstr
+#define DYNARRAY_ELEMENT char
+#define DYNARRAY_PREFIX zstr_
+#define DYNARRAY_INITIAL_SIZE 128
+#include <malloc/dynarray-skeleton.c>
+
+#include <malloc.h>
+#include <mcheck.h>
+#include <stdint.h>
+#include <support/check.h>
+#include <support/support.h>
+
+enum { max_count = 20 };
+
+/* Test dynamic arrays with int elements (no automatic deallocation
+ for elements). */
+static void
+test_int (void)
+{
+ /* Empty array. */
+ {
+ struct dynarray_int dyn;
+ dynarray_int_init (&dyn);
+ CHECK_EMPTY (int, &dyn);
+ }
+
+ /* Empty array with finalization. */
+ {
+ struct dynarray_int dyn;
+ dynarray_int_init (&dyn);
+ CHECK_INIT_STATE (int, &dyn);
+ struct int_array result = { (int *) (uintptr_t) -1, -1 };
+ TEST_VERIFY_EXIT (dynarray_int_finalize (&dyn, &result));
+ CHECK_INIT_STATE (int, &dyn);
+ TEST_VERIFY_EXIT (result.array == NULL);
+ TEST_VERIFY_EXIT (result.length == 0);
+ }
+
+ /* Non-empty array tests.
+
+ do_add: Switch between emplace (false) and add (true).
+ do_finalize: Perform finalize call at the end.
+ do_clear: Perform clear call at the end.
+ do_remove_last: Perform remove_last call after adding elements.
+ count: Number of elements added to the array. */
+ for (int do_add = 0; do_add < 2; ++do_add)
+ for (int do_finalize = 0; do_finalize < 2; ++do_finalize)
+ for (int do_clear = 0; do_clear < 2; ++do_clear)
+ for (int do_remove_last = 0; do_remove_last < 2; ++do_remove_last)
+ for (unsigned int count = 0; count < max_count; ++count)
+ {
+ if (do_remove_last && count == 0)
+ continue;
+ unsigned int base = count * count;
+ struct dynarray_int dyn;
+ dynarray_int_init (&dyn);
+ for (unsigned int i = 0; i < count; ++i)
+ {
+ if (do_add)
+ dynarray_int_add (&dyn, base + i);
+ else
+ {
+ int *place = dynarray_int_emplace (&dyn);
+ TEST_VERIFY_EXIT (place != NULL);
+ *place = base + i;
+ }
+ TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
+ TEST_VERIFY_EXIT (dynarray_int_size (&dyn) == i + 1);
+ TEST_VERIFY_EXIT (dynarray_int_size (&dyn)
+ <= dyn.dynarray_header.allocated);
+ }
+ TEST_VERIFY_EXIT (dynarray_int_size (&dyn) == count);
+ TEST_VERIFY_EXIT (count <= dyn.dynarray_header.allocated);
+ if (count > 0)
+ {
+ TEST_VERIFY (dynarray_int_begin (&dyn)
+ == dynarray_int_at (&dyn, 0));
+ TEST_VERIFY (dynarray_int_end (&dyn)
+ == dynarray_int_at (&dyn, count - 1) + 1);
+ }
+ unsigned final_count;
+ bool heap_array = dyn.dynarray_header.array != dyn.scratch;
+ if (do_remove_last)
+ {
+ dynarray_int_remove_last (&dyn);
+ if (count == 0)
+ final_count = 0;
+ else
+ final_count = count - 1;
+ }
+ else
+ final_count = count;
+ if (final_count > 0)
+ {
+ TEST_VERIFY (dynarray_int_begin (&dyn)
+ == dynarray_int_at (&dyn, 0));
+ TEST_VERIFY (dynarray_int_end (&dyn)
+ == dynarray_int_at (&dyn, final_count - 1) + 1);
+ }
+ if (do_clear)
+ {
+ dynarray_int_clear (&dyn);
+ final_count = 0;
+ }
+ TEST_VERIFY_EXIT (!dynarray_int_has_failed (&dyn));
+ TEST_VERIFY_EXIT ((dyn.dynarray_header.array != dyn.scratch)
+ == heap_array);
+ TEST_VERIFY_EXIT (dynarray_int_size (&dyn) == final_count);
+ TEST_VERIFY_EXIT (dyn.dynarray_header.allocated >= final_count);
+ if (!do_clear)
+ for (unsigned int i = 0; i < final_count; ++i)
+ TEST_VERIFY_EXIT (*dynarray_int_at (&dyn, i) == base + i);
+ if (do_finalize)
+ {
+ struct int_array result = { (int *) (uintptr_t) -1, -1 };
+ TEST_VERIFY_EXIT (dynarray_int_finalize (&dyn, &result));
+ CHECK_INIT_STATE (int, &dyn);
+ TEST_VERIFY_EXIT (result.length == final_count);
+ if (final_count == 0)
+ TEST_VERIFY_EXIT (result.array == NULL);
+ else
+ {
+ TEST_VERIFY_EXIT (result.array != NULL);
+ TEST_VERIFY_EXIT (result.array != (int *) (uintptr_t) -1);
+ TEST_VERIFY_EXIT
+ (malloc_usable_size (result.array)
+ >= final_count * sizeof (result.array[0]));
+ for (unsigned int i = 0; i < final_count; ++i)
+ TEST_VERIFY_EXIT (result.array[i] == base + i);
+ free (result.array);
+ }
+ }
+ else /* !do_finalize */
+ {
+ dynarray_int_free (&dyn);
+ CHECK_INIT_STATE (int, &dyn);
+ }
+ }
+}
+
+/* Test dynamic arrays with char * elements (with automatic
+ deallocation of the pointed-to strings). */
+static void
+test_str (void)
+{
+ /* Empty array. */
+ {
+ struct dynarray_str dyn;
+ dynarray_str_init (&dyn);
+ CHECK_EMPTY (str, &dyn);
+ }
+
+ /* Empty array with finalization. */
+ {
+ struct dynarray_str dyn;
+ dynarray_str_init (&dyn);
+ TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
+ struct str_array result = { (char **) (uintptr_t) -1, -1 };
+ TEST_VERIFY_EXIT (dynarray_str_finalize (&dyn, &result));
+ CHECK_INIT_STATE (str, &dyn);
+ TEST_VERIFY_EXIT (result.array == NULL);
+ TEST_VERIFY_EXIT (result.length == 0);
+ }
+
+ /* Non-empty array tests.
+
+ do_add: Switch between emplace (false) and add (true).
+ do_finalize: Perform finalize call at the end.
+ do_clear: Perform clear call at the end.
+ do_remove_last: Perform remove_last call after adding elements.
+ count: Number of elements added to the array. */
+ for (int do_add = 0; do_add < 2; ++do_add)
+ for (int do_finalize = 0; do_finalize < 2; ++do_finalize)
+ for (int do_clear = 0; do_clear < 2; ++do_clear)
+ for (int do_remove_last = 0; do_remove_last < 2; ++do_remove_last)
+ for (unsigned int count = 0; count < max_count; ++count)
+ {
+ if (do_remove_last && count == 0)
+ continue;
+ unsigned int base = count * count;
+ struct dynarray_str dyn;
+ dynarray_str_init (&dyn);
+ for (unsigned int i = 0; i < count; ++i)
+ {
+ char *item = xasprintf ("%d", base + i);
+ if (do_add)
+ dynarray_str_add (&dyn, item);
+ else
+ {
+ char **place = dynarray_str_emplace (&dyn);
+ TEST_VERIFY_EXIT (place != NULL);
+ TEST_VERIFY_EXIT (*place == NULL);
+ *place = item;
+ }
+ TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
+ TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == i + 1);
+ TEST_VERIFY_EXIT (dynarray_str_size (&dyn)
+ <= dyn.dynarray_header.allocated);
+ }
+ TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == count);
+ TEST_VERIFY_EXIT (count <= dyn.dynarray_header.allocated);
+ if (count > 0)
+ {
+ TEST_VERIFY (dynarray_str_begin (&dyn)
+ == dynarray_str_at (&dyn, 0));
+ TEST_VERIFY (dynarray_str_end (&dyn)
+ == dynarray_str_at (&dyn, count - 1) + 1);
+ }
+ unsigned final_count;
+ bool heap_array = dyn.dynarray_header.array != dyn.scratch;
+ if (do_remove_last)
+ {
+ dynarray_str_remove_last (&dyn);
+ if (count == 0)
+ final_count = 0;
+ else
+ final_count = count - 1;
+ }
+ else
+ final_count = count;
+ if (final_count > 0)
+ {
+ TEST_VERIFY (dynarray_str_begin (&dyn)
+ == dynarray_str_at (&dyn, 0));
+ TEST_VERIFY (dynarray_str_end (&dyn)
+ == dynarray_str_at (&dyn, final_count - 1) + 1);
+ }
+ if (do_clear)
+ {
+ dynarray_str_clear (&dyn);
+ final_count = 0;
+ }
+ TEST_VERIFY_EXIT (!dynarray_str_has_failed (&dyn));
+ TEST_VERIFY_EXIT ((dyn.dynarray_header.array != dyn.scratch)
+ == heap_array);
+ TEST_VERIFY_EXIT (dynarray_str_size (&dyn) == final_count);
+ TEST_VERIFY_EXIT (dyn.dynarray_header.allocated >= final_count);
+ if (!do_clear)
+ for (unsigned int i = 0; i < count - do_remove_last; ++i)
+ {
+ char *expected = xasprintf ("%d", base + i);
+ const char *actual = *dynarray_str_at (&dyn, i);
+ TEST_VERIFY_EXIT (strcmp (actual, expected) == 0);
+ free (expected);
+ }
+ if (do_finalize)
+ {
+ struct str_array result = { (char **) (uintptr_t) -1, -1 };
+ TEST_VERIFY_EXIT (dynarray_str_finalize (&dyn, &result));
+ CHECK_INIT_STATE (str, &dyn);
+ TEST_VERIFY_EXIT (result.length == final_count);
+ if (final_count == 0)
+ TEST_VERIFY_EXIT (result.array == NULL);
+ else
+ {
+ TEST_VERIFY_EXIT (result.array != NULL);
+ TEST_VERIFY_EXIT (result.array
+ != (char **) (uintptr_t) -1);
+ TEST_VERIFY_EXIT (result.length
+ == count - do_remove_last);
+ TEST_VERIFY_EXIT
+ (malloc_usable_size (result.array)
+ >= final_count * sizeof (result.array[0]));
+ for (unsigned int i = 0; i < count - do_remove_last; ++i)
+ {
+ char *expected = xasprintf ("%d", base + i);
+ char *actual = result.array[i];
+ TEST_VERIFY_EXIT (strcmp (actual, expected) == 0);
+ free (expected);
+ free (actual);
+ }
+ free (result.array);
+ }
+ }
+ else /* !do_finalize */
+ {
+ dynarray_str_free (&dyn);
+ CHECK_INIT_STATE (str, &dyn);
+ }
+ }
+
+ /* Test resizing. */
+ {
+ enum { count = 2131 };
+ struct dynarray_str dyn;
+ dynarray_str_init (&dyn);
+
+ /* From length 0 to length 1. */
+ TEST_VERIFY (dynarray_str_resize (&dyn, 1));
+ TEST_VERIFY (dynarray_str_size (&dyn) == 1);
+ TEST_VERIFY (*dynarray_str_at (&dyn, 0) == NULL);
+ *dynarray_str_at (&dyn, 0) = xstrdup ("allocated");
+ dynarray_str_free (&dyn);
+
+ /* From length 0 to length 1 and 2. */
+ TEST_VERIFY (dynarray_str_resize (&dyn, 1));
+ TEST_VERIFY (dynarray_str_size (&dyn) == 1);
+ TEST_VERIFY (*dynarray_str_at (&dyn, 0) == NULL);
+ *dynarray_str_at (&dyn, 0) = xstrdup ("allocated0");
+ TEST_VERIFY (dynarray_str_resize (&dyn, 2));
+ TEST_VERIFY (dynarray_str_size (&dyn) == 2);
+ TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 0), "allocated0") == 0);
+ TEST_VERIFY (*dynarray_str_at (&dyn, 1) == NULL);
+ *dynarray_str_at (&dyn, 1) = xstrdup ("allocated1");
+ TEST_VERIFY (dynarray_str_resize (&dyn, count));
+ TEST_VERIFY (dynarray_str_size (&dyn) == count);
+ TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 0), "allocated0") == 0);
+ TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 1), "allocated1") == 0);
+ for (int i = 2; i < count; ++i)
+ TEST_VERIFY (*dynarray_str_at (&dyn, i) == NULL);
+ *dynarray_str_at (&dyn, count - 1) = xstrdup ("allocated2");
+ TEST_VERIFY (dynarray_str_resize (&dyn, 3));
+ TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 0), "allocated0") == 0);
+ TEST_VERIFY (strcmp (*dynarray_str_at (&dyn, 1), "allocated1") == 0);
+ TEST_VERIFY (*dynarray_str_at (&dyn, 2) == NULL);
+ dynarray_str_free (&dyn);
+ }
+}
+
+/* Verify that DYNARRAY_ELEMENT_INIT has an effect. */
+static void
+test_long_init (void)
+{
+ enum { count = 2131 };
+ {
+ struct dynarray_long dyn;
+ dynarray_long_init (&dyn);
+ for (int i = 0; i < count; ++i)
+ {
+ long *place = dynarray_long_emplace (&dyn);
+ TEST_VERIFY_EXIT (place != NULL);
+ TEST_VERIFY (*place == 17);
+ }
+ TEST_VERIFY (dynarray_long_size (&dyn) == count);
+ for (int i = 0; i < count; ++i)
+ TEST_VERIFY (*dynarray_long_at (&dyn, i) == 17);
+ dynarray_long_free (&dyn);
+
+ TEST_VERIFY (dynarray_long_resize (&dyn, 1));
+ TEST_VERIFY (dynarray_long_size (&dyn) == 1);
+ TEST_VERIFY (*dynarray_long_at (&dyn, 0) == 17);
+ *dynarray_long_at (&dyn, 0) = 18;
+ dynarray_long_free (&dyn);
+ TEST_VERIFY (dynarray_long_resize (&dyn, 1));
+ TEST_VERIFY (dynarray_long_size (&dyn) == 1);
+ TEST_VERIFY (*dynarray_long_at (&dyn, 0) == 17);
+ TEST_VERIFY (dynarray_long_resize (&dyn, 2));
+ TEST_VERIFY (dynarray_long_size (&dyn) == 2);
+ TEST_VERIFY (*dynarray_long_at (&dyn, 0) == 17);
+ TEST_VERIFY (*dynarray_long_at (&dyn, 1) == 17);
+ *dynarray_long_at (&dyn, 0) = 18;
+ TEST_VERIFY (dynarray_long_resize (&dyn, count));
+ TEST_VERIFY (dynarray_long_size (&dyn) == count);
+ TEST_VERIFY (*dynarray_long_at (&dyn, 0) == 18);
+ for (int i = 1; i < count; ++i)
+ TEST_VERIFY (*dynarray_long_at (&dyn, i) == 17);
+ dynarray_long_free (&dyn);
+ }
+
+ /* Similar, but without an on-stack scratch region
+ (DYNARRAY_INITIAL_SIZE is 0). */
+ {
+ struct dynarray_long_noscratch dyn;
+ dynarray_long_noscratch_init (&dyn);
+ struct long_array result;
+ TEST_VERIFY_EXIT (dynarray_long_noscratch_finalize (&dyn, &result));
+ TEST_VERIFY (result.array == NULL);
+ TEST_VERIFY (result.length == 0);
+
+ /* Test with one element. */
+ {
+ long *place = dynarray_long_noscratch_emplace (&dyn);
+ TEST_VERIFY_EXIT (place != NULL);
+ TEST_VERIFY (*place == 23);
+ }
+ TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == 1);
+ TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 23);
+ TEST_VERIFY_EXIT (dynarray_long_noscratch_finalize (&dyn, &result));
+ TEST_VERIFY_EXIT (result.array != NULL);
+ TEST_VERIFY (result.length == 1);
+ TEST_VERIFY (result.array[0] == 23);
+ free (result.array);
+
+ for (int i = 0; i < count; ++i)
+ {
+ long *place = dynarray_long_noscratch_emplace (&dyn);
+ TEST_VERIFY_EXIT (place != NULL);
+ TEST_VERIFY (*place == 23);
+ if (i == 0)
+ *place = 29;
+ }
+ TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == count);
+ TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 29);
+ for (int i = 1; i < count; ++i)
+ TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, i) == 23);
+ TEST_VERIFY_EXIT (dynarray_long_noscratch_finalize (&dyn, &result));
+ TEST_VERIFY_EXIT (result.array != NULL);
+ TEST_VERIFY (result.length == count);
+ TEST_VERIFY (result.array[0] == 29);
+ for (int i = 1; i < count; ++i)
+ TEST_VERIFY (result.array[i] == 23);
+ free (result.array);
+
+ TEST_VERIFY (dynarray_long_noscratch_resize (&dyn, 1));
+ TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == 1);
+ TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 23);
+ *dynarray_long_noscratch_at (&dyn, 0) = 24;
+ dynarray_long_noscratch_free (&dyn);
+ TEST_VERIFY (dynarray_long_noscratch_resize (&dyn, 1));
+ TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == 1);
+ TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 23);
+ TEST_VERIFY (dynarray_long_noscratch_resize (&dyn, 2));
+ TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == 2);
+ TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 23);
+ TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 1) == 23);
+ *dynarray_long_noscratch_at (&dyn, 0) = 24;
+ TEST_VERIFY (dynarray_long_noscratch_resize (&dyn, count));
+ TEST_VERIFY (dynarray_long_noscratch_size (&dyn) == count);
+ TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, 0) == 24);
+ for (int i = 1; i < count; ++i)
+ TEST_VERIFY (*dynarray_long_noscratch_at (&dyn, i) == 23);
+ dynarray_long_noscratch_free (&dyn);
+ }
+}
+
+/* Test overflow in resize. */
+static void
+test_long_overflow (void)
+{
+ {
+ struct dynarray_long dyn;
+ dynarray_long_init (&dyn);
+ errno = EINVAL;
+ TEST_VERIFY (!dynarray_long_resize
+ (&dyn, (SIZE_MAX / sizeof (long)) + 1));
+ TEST_VERIFY (errno == ENOMEM);
+ TEST_VERIFY (dynarray_long_has_failed (&dyn));
+ }
+
+ {
+ struct dynarray_long_noscratch dyn;
+ dynarray_long_noscratch_init (&dyn);
+ errno = EINVAL;
+ TEST_VERIFY (!dynarray_long_noscratch_resize
+ (&dyn, (SIZE_MAX / sizeof (long)) + 1));
+ TEST_VERIFY (errno == ENOMEM);
+ TEST_VERIFY (dynarray_long_noscratch_has_failed (&dyn));
+ }
+}
+
+/* Test NUL-terminated string construction with the add function and
+ the simple finalize function. */
+static void
+test_zstr (void)
+{
+ /* Totally empty string (no NUL termination). */
+ {
+ struct zstr s;
+ zstr_init (&s);
+ char *result = zstr_finalize (&s, NULL);
+ TEST_VERIFY (result == NULL);
+ TEST_VERIFY (zstr_size (&s) == 0);
+ size_t length = 1;
+ result = zstr_finalize (&s, &length);
+ TEST_VERIFY (result == NULL);
+ TEST_VERIFY (length == 0);
+ TEST_VERIFY (zstr_size (&s) == 0);
+ }
+
+ /* Empty string. */
+ {
+ struct zstr s;
+ zstr_init (&s);
+ zstr_add (&s, '\0');
+ char *result = zstr_finalize (&s, NULL);
+ TEST_VERIFY_EXIT (result != NULL);
+ TEST_VERIFY (*result == '\0');
+ TEST_VERIFY (zstr_size (&s) == 0);
+ free (result);
+
+ zstr_add (&s, '\0');
+ size_t length = 1;
+ result = zstr_finalize (&s, &length);
+ TEST_VERIFY_EXIT (result != NULL);
+ TEST_VERIFY (*result == '\0');
+ TEST_VERIFY (length == 1);
+ TEST_VERIFY (zstr_size (&s) == 0);
+ free (result);
+ }
+
+ /* A few characters. */
+ {
+ struct zstr s;
+ zstr_init (&s);
+ zstr_add (&s, 'A');
+ zstr_add (&s, 'b');
+ zstr_add (&s, 'c');
+ zstr_add (&s, '\0');
+ char *result = zstr_finalize (&s, NULL);
+ TEST_VERIFY_EXIT (result != NULL);
+ TEST_VERIFY (strcmp (result, "Abc") == 0);
+ TEST_VERIFY (zstr_size (&s) == 0);
+ free (result);
+
+ zstr_add (&s, 'X');
+ zstr_add (&s, 'y');
+ zstr_add (&s, 'z');
+ zstr_add (&s, '\0');
+ size_t length = 1;
+ result = zstr_finalize (&s, &length);
+ TEST_VERIFY_EXIT (result != NULL);
+ TEST_VERIFY (strcmp (result, "Xyz") == 0);
+ TEST_VERIFY (length == 4);
+ TEST_VERIFY (zstr_size (&s) == 0);
+ free (result);
+ }
+}
+
+static int
+do_test (void)
+{
+ mtrace ();
+ test_int ();
+ test_str ();
+ test_long_init ();
+ test_long_overflow ();
+ test_zstr ();
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/malloc/tst-interpose-aux-nothread.c b/malloc/tst-interpose-aux-nothread.c
new file mode 100644
index 0000000000..b4ecd52e6a
--- /dev/null
+++ b/malloc/tst-interpose-aux-nothread.c
@@ -0,0 +1,20 @@
+/* Interposed malloc, version without threading support.
+ Copyright (C) 2016-2018 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..32a668fa97
--- /dev/null
+++ b/malloc/tst-interpose-aux-thread.c
@@ -0,0 +1,20 @@
+/* Interposed malloc, version with threading support.
+ Copyright (C) 2016-2018 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..bb37a39742
--- /dev/null
+++ b/malloc/tst-interpose-aux.c
@@ -0,0 +1,271 @@
+/* Minimal malloc implementation for interposition tests.
+ Copyright (C) 2016-2018 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 <stdint.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");
+ /* 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");
+ free (ptr);
+ /* 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..051e9fbf1b
--- /dev/null
+++ b/malloc/tst-interpose-aux.h
@@ -0,0 +1,30 @@
+/* Statistics interface for the minimal malloc implementation.
+ Copyright (C) 2016-2018 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..c85263effb
--- /dev/null
+++ b/malloc/tst-interpose-nothread.c
@@ -0,0 +1,20 @@
+/* Malloc interposition test, dynamically-linked version without threads.
+ Copyright (C) 2016-2018 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..714676052e
--- /dev/null
+++ b/malloc/tst-interpose-skeleton.c
@@ -0,0 +1,204 @@
+/* Test driver for malloc interposition tests.
+ Copyright (C) 2016-2018 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..84b21572b5
--- /dev/null
+++ b/malloc/tst-interpose-static-nothread.c
@@ -0,0 +1,19 @@
+/* Malloc interposition test, static version without threads.
+ Copyright (C) 2016-2018 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..98cf7df49b
--- /dev/null
+++ b/malloc/tst-interpose-static-thread.c
@@ -0,0 +1,19 @@
+/* Malloc interposition test, static version with threads.
+ Copyright (C) 2016-2018 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..62e8922d84
--- /dev/null
+++ b/malloc/tst-interpose-thread.c
@@ -0,0 +1,20 @@
+/* Malloc interposition test, dynamically-linked version with threads.
+ Copyright (C) 2016-2018 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/malloc/tst-malloc-backtrace.c b/malloc/tst-malloc-backtrace.c
index 3aee7fdb28..171cfcefb8 100644
--- a/malloc/tst-malloc-backtrace.c
+++ b/malloc/tst-malloc-backtrace.c
@@ -1,5 +1,5 @@
/* Verify that backtrace does not deadlock on itself on memory corruption.
- Copyright (C) 2015-2016 Free Software Foundation, Inc.
+ Copyright (C) 2015-2018 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,9 +16,11 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
-
+#include <signal.h>
#include <stdlib.h>
+#include <support/support.h>
+
#define SIZE 4096
/* Wrap free with a function to prevent gcc from optimizing it out. */
@@ -30,13 +32,6 @@ call_free (void *ptr)
*(size_t *)(ptr - sizeof (size_t)) = 1;
}
-int do_test (void);
-
-#define TEST_FUNCTION do_test ()
-#define EXPECTED_SIGNAL SIGABRT
-
-#include "../test-skeleton.c"
-
int
do_test (void)
{
@@ -53,3 +48,6 @@ do_test (void)
doesn't optimize out that malloc call. */
return (ptr1 == ptr2);
}
+
+#define EXPECTED_SIGNAL SIGABRT
+#include <support/test-driver.c>
diff --git a/malloc/tst-malloc-fork-deadlock.c b/malloc/tst-malloc-fork-deadlock.c
new file mode 100644
index 0000000000..8abaebfd9a
--- /dev/null
+++ b/malloc/tst-malloc-fork-deadlock.c
@@ -0,0 +1,206 @@
+/* Test concurrent fork, getline, and fflush (NULL).
+ Copyright (C) 2016-2018 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>
+
+#include <support/xthread.h>
+#include <support/temp_file.h>
+#include <support/test-driver.h>
+
+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)
+ threads[i] = xpthread_create (NULL, func, NULL);
+}
+
+static void
+join_threads (pthread_t *threads, size_t count)
+{
+ for (size_t i = 0; i < count; ++i)
+ xpthread_join (threads[i]);
+}
+
+/* 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 > DEFAULT_TIMEOUT)
+ timeout = DEFAULT_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;
+}
+
+#include <support/test-driver.c>
diff --git a/malloc/tst-malloc-stats-cancellation.c b/malloc/tst-malloc-stats-cancellation.c
new file mode 100644
index 0000000000..9a8f475830
--- /dev/null
+++ b/malloc/tst-malloc-stats-cancellation.c
@@ -0,0 +1,216 @@
+/* Bug 22830: malloc_stats fails to re-enable cancellation on exit.
+ Copyright (C) 2018 Free Software Foundation.
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. This file is offered as-is,
+ without any warranty. */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <pthread.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <malloc.h>
+
+static void *
+test_threadproc (void *gatep)
+{
+ /* When we are released from the barrier, there is a cancellation
+ request pending for this thread. N.B. pthread_barrier_wait is
+ not itself a cancellation point (oddly enough). */
+ pthread_barrier_wait ((pthread_barrier_t *)gatep);
+ malloc_stats ();
+ fputs ("this call should trigger cancellation\n", stderr);
+ return 0;
+}
+
+/* We cannot replace stderr with a memstream because writes to memstreams
+ do not trigger cancellation. Instead, main swaps out fd 2 to point to
+ a pipe, and this thread reads from the pipe and writes to a memstream
+ until EOF, then returns the data accumulated in the memstream. main
+ can't do that itself because, when the test thread gets cancelled,
+ it doesn't close the pipe. */
+
+struct buffer_tp_args
+{
+ int ifd;
+ FILE *real_stderr;
+};
+
+static void *
+buffer_threadproc (void *argp)
+{
+ struct buffer_tp_args *args = argp;
+ int ifd = args->ifd;
+ char block[BUFSIZ], *p;
+ ssize_t nread;
+ size_t nwritten;
+
+ char *obuf = 0;
+ size_t obufsz = 0;
+ FILE *ofp = open_memstream (&obuf, &obufsz);
+ if (!ofp)
+ {
+ fprintf (args->real_stderr,
+ "buffer_threadproc: open_memstream: %s\n", strerror (errno));
+ return 0;
+ }
+
+ while ((nread = read (ifd, block, BUFSIZ)) > 0)
+ {
+ p = block;
+ do
+ {
+ nwritten = fwrite_unlocked (p, 1, nread, ofp);
+ if (nwritten == 0)
+ {
+ fprintf (args->real_stderr,
+ "buffer_threadproc: fwrite_unlocked: %s\n",
+ strerror (errno));
+ return 0;
+ }
+ nread -= nwritten;
+ p += nwritten;
+ }
+ while (nread > 0);
+ }
+ if (nread == -1)
+ {
+ fprintf (args->real_stderr, "buffer_threadproc: read: %s\n",
+ strerror (errno));
+ return 0;
+ }
+ close (ifd);
+ fclose (ofp);
+ return obuf;
+}
+
+
+int
+main (void)
+{
+ int result = 0, err, real_stderr_fd, bufpipe[2];
+ pthread_t t_thr, b_thr;
+ pthread_barrier_t gate;
+ void *rv;
+ FILE *real_stderr;
+ char *obuf;
+ void *obuf_v;
+ struct buffer_tp_args b_args;
+
+ real_stderr_fd = dup (2);
+ if (real_stderr_fd == -1)
+ {
+ perror ("dup");
+ return 2;
+ }
+ real_stderr = fdopen(real_stderr_fd, "w");
+ if (!real_stderr)
+ {
+ perror ("fdopen");
+ return 2;
+ }
+ if (setvbuf (real_stderr, 0, _IOLBF, 0))
+ {
+ perror ("setvbuf(real_stderr)");
+ return 2;
+ }
+
+ if (pipe (bufpipe))
+ {
+ perror ("pipe");
+ return 2;
+ }
+
+ /* Below this point, nobody other than the test_threadproc should use
+ the normal stderr. */
+ if (dup2 (bufpipe[1], 2) == -1)
+ {
+ fprintf (real_stderr, "dup2: %s\n", strerror (errno));
+ return 2;
+ }
+ close (bufpipe[1]);
+
+ b_args.ifd = bufpipe[0];
+ b_args.real_stderr = real_stderr;
+ err = pthread_create (&b_thr, 0, buffer_threadproc, &b_args);
+ if (err)
+ {
+ fprintf (real_stderr, "pthread_create(buffer_thr): %s\n",
+ strerror (err));
+ return 2;
+ }
+
+ err = pthread_barrier_init (&gate, 0, 2);
+ if (err)
+ {
+ fprintf (real_stderr, "pthread_barrier_init: %s\n", strerror (err));
+ return 2;
+ }
+
+ err = pthread_create (&t_thr, 0, test_threadproc, &gate);
+ if (err)
+ {
+ fprintf (real_stderr, "pthread_create(test_thr): %s\n", strerror (err));
+ return 2;
+ }
+
+ err = pthread_cancel (t_thr);
+ if (err)
+ {
+ fprintf (real_stderr, "pthread_cancel: %s\n", strerror (err));
+ return 2;
+ }
+
+ pthread_barrier_wait (&gate); /* cannot fail */
+
+ err = pthread_join (t_thr, &rv);
+ if (err)
+ {
+ fprintf (real_stderr, "pthread_join(test_thr): %s\n", strerror (err));
+ return 2;
+ }
+
+ /* Closing the normal stderr releases the buffer_threadproc from its
+ loop. */
+ fclose (stderr);
+ err = pthread_join (b_thr, &obuf_v);
+ if (err)
+ {
+ fprintf (real_stderr, "pthread_join(buffer_thr): %s\n", strerror (err));
+ return 2;
+ }
+ obuf = obuf_v;
+ if (obuf == 0)
+ return 2; /* error within buffer_threadproc, already reported */
+
+ if (rv != PTHREAD_CANCELED)
+ {
+ fputs ("FAIL: thread was not cancelled\n", real_stderr);
+ result = 1;
+ }
+ /* obuf should have received all of the text printed by malloc_stats,
+ but not the text printed by the final call to fputs. */
+ if (!strstr (obuf, "max mmap bytes"))
+ {
+ fputs ("FAIL: malloc_stats output incomplete\n", real_stderr);
+ result = 1;
+ }
+ if (strstr (obuf, "this call should trigger cancellation"))
+ {
+ fputs ("FAIL: fputs produced output\n", real_stderr);
+ result = 1;
+ }
+
+ if (result == 1)
+ {
+ fputs ("--- output from thread below ---\n", real_stderr);
+ fputs (obuf, real_stderr);
+ }
+ return result;
+}
diff --git a/malloc/tst-malloc-tcache-leak.c b/malloc/tst-malloc-tcache-leak.c
new file mode 100644
index 0000000000..7d9731435e
--- /dev/null
+++ b/malloc/tst-malloc-tcache-leak.c
@@ -0,0 +1,113 @@
+/* Bug 22111: Test that threads do not leak their per thread cache.
+ Copyright (C) 2015-2018 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/>. */
+
+/* The point of this test is to start and exit a large number of
+ threads, while at the same time looking to see if the used
+ memory grows with each round of threads run. If the memory
+ grows above some linear bound we declare the test failed and
+ that the malloc implementation is leaking memory with each
+ thread. This is a good indicator that the thread local cache
+ is leaking chunks. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <pthread.h>
+#include <assert.h>
+
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xthread.h>
+
+void *
+worker (void *data)
+{
+ void *ret;
+ /* Allocate an arbitrary amount of memory that is known to fit into
+ the thread local cache (tcache). If we have at least 64 bins
+ (default e.g. TCACHE_MAX_BINS) we should be able to allocate 32
+ bytes and force malloc to fill the tcache. We are assuming tcahce
+ init happens at the first small alloc, but it might in the future
+ be deferred to some other point. Therefore to future proof this
+ test we include a full alloc/free/alloc cycle for the thread. We
+ need a compiler barrier to avoid the removal of the useless
+ alloc/free. We send some memory back to main to have the memory
+ freed after the thread dies, as just another check that the chunks
+ that were previously in the tcache are still OK to free after
+ thread death. */
+ ret = xmalloc (32);
+ __asm__ volatile ("" ::: "memory");
+ free (ret);
+ return (void *) xmalloc (32);
+}
+
+static int
+do_test (void)
+{
+ pthread_t *thread;
+ struct mallinfo info_before, info_after;
+ void *retval;
+
+ /* This is an arbitrary choice. We choose a total of THREADS
+ threads created and joined. This gives us enough iterations to
+ show a leak. */
+ int threads = 100000;
+
+ /* Avoid there being 0 malloc'd data at this point by allocating the
+ pthread_t required to run the test. */
+ thread = (pthread_t *) xcalloc (1, sizeof (pthread_t));
+
+ info_before = mallinfo ();
+
+ assert (info_before.uordblks != 0);
+
+ printf ("INFO: %d (bytes) are in use before starting threads.\n",
+ info_before.uordblks);
+
+ for (int loop = 0; loop < threads; loop++)
+ {
+ *thread = xpthread_create (NULL, worker, NULL);
+ retval = xpthread_join (*thread);
+ free (retval);
+ }
+
+ info_after = mallinfo ();
+ printf ("INFO: %d (bytes) are in use after all threads joined.\n",
+ info_after.uordblks);
+
+ /* We need to compare the memory in use before and the memory in use
+ after starting and joining THREADS threads. We almost always grow
+ memory slightly, but not much. Consider that if even 1-byte leaked
+ per thread we'd have THREADS bytes of additional memory, and in
+ general the in-use at the start of main is quite low. We will
+ always leak a full malloc chunk, and never just 1-byte, therefore
+ anything above "+ threads" from the start (constant offset) is a
+ leak. Obviously this assumes no thread-related malloc'd internal
+ libc data structures persist beyond the thread death, and any that
+ did would limit the number of times you could call pthread_create,
+ which is a QoI we'd want to detect and fix. */
+ if (info_after.uordblks > (info_before.uordblks + threads))
+ FAIL_EXIT1 ("Memory usage after threads is too high.\n");
+
+ /* Did not detect excessive memory usage. */
+ free (thread);
+ exit (0);
+}
+
+#define TIMEOUT 50
+#include <support/test-driver.c>
diff --git a/malloc/tst-malloc-thread-exit.c b/malloc/tst-malloc-thread-exit.c
index f4aa21af86..419a65d472 100644
--- a/malloc/tst-malloc-thread-exit.c
+++ b/malloc/tst-malloc-thread-exit.c
@@ -1,5 +1,5 @@
/* Test malloc with concurrent thread termination.
- Copyright (C) 2015-2016 Free Software Foundation, Inc.
+ Copyright (C) 2015-2018 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
@@ -26,13 +26,16 @@
particularly related to the arena free list. */
#include <errno.h>
+#include <malloc.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
-#define TIMEOUT 7
+#include <support/support.h>
+#include <support/xthread.h>
+#include <support/test-driver.h>
static bool termination_requested;
static int inner_thread_count = 4;
@@ -49,19 +52,8 @@ static void *
malloc_first_thread (void * closure)
{
pthread_barrier_t *barrier = closure;
- void *ptr = malloc (malloc_size);
- if (ptr == NULL)
- {
- printf ("error: malloc: %m\n");
- abort ();
- }
- int ret = pthread_barrier_wait (barrier);
- if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)
- {
- errno = ret;
- printf ("error: pthread_barrier_wait: %m\n");
- abort ();
- }
+ void *ptr = xmalloc (malloc_size);
+ xpthread_barrier_wait (barrier);
unoptimized_free (ptr);
return NULL;
}
@@ -70,19 +62,8 @@ static void *
wait_first_thread (void * closure)
{
pthread_barrier_t *barrier = closure;
- int ret = pthread_barrier_wait (barrier);
- if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)
- {
- errno = ret;
- printf ("error: pthread_barrier_wait: %m\n");
- abort ();
- }
- void *ptr = malloc (malloc_size);
- if (ptr == NULL)
- {
- printf ("error: malloc: %m\n");
- abort ();
- }
+ xpthread_barrier_wait (barrier);
+ void *ptr = xmalloc (malloc_size);
unoptimized_free (ptr);
return NULL;
}
@@ -90,23 +71,11 @@ wait_first_thread (void * closure)
static void *
outer_thread (void *closure)
{
- pthread_t *threads = calloc (sizeof (*threads), inner_thread_count);
- if (threads == NULL)
- {
- printf ("error: calloc: %m\n");
- abort ();
- }
-
+ pthread_t *threads = xcalloc (sizeof (*threads), inner_thread_count);
while (!__atomic_load_n (&termination_requested, __ATOMIC_RELAXED))
{
pthread_barrier_t barrier;
- int ret = pthread_barrier_init (&barrier, NULL, inner_thread_count + 1);
- if (ret != 0)
- {
- errno = ret;
- printf ("pthread_barrier_init: %m\n");
- abort ();
- }
+ xpthread_barrier_init (&barrier, NULL, inner_thread_count + 1);
for (int i = 0; i < inner_thread_count; ++i)
{
void *(*func) (void *);
@@ -114,38 +83,12 @@ outer_thread (void *closure)
func = malloc_first_thread;
else
func = wait_first_thread;
- ret = pthread_create (threads + i, NULL, func, &barrier);
- if (ret != 0)
- {
- errno = ret;
- printf ("error: pthread_create: %m\n");
- abort ();
- }
- }
- ret = pthread_barrier_wait (&barrier);
- if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)
- {
- errno = ret;
- printf ("pthread_wait: %m\n");
- abort ();
+ threads[i] = xpthread_create (NULL, func, &barrier);
}
+ xpthread_barrier_wait (&barrier);
for (int i = 0; i < inner_thread_count; ++i)
- {
- ret = pthread_join (threads[i], NULL);
- if (ret != 0)
- {
- ret = errno;
- printf ("error: pthread_join: %m\n");
- abort ();
- }
- }
- ret = pthread_barrier_destroy (&barrier);
- if (ret != 0)
- {
- ret = errno;
- printf ("pthread_barrier_destroy: %m\n");
- abort ();
- }
+ xpthread_join (threads[i]);
+ xpthread_barrier_destroy (&barrier);
}
free (threads);
@@ -156,38 +99,24 @@ outer_thread (void *closure)
static int
do_test (void)
{
- /* The number of top-level threads should be equal to the number of
- arenas. See arena_get2. */
- long outer_thread_count = sysconf (_SC_NPROCESSORS_ONLN);
- if (outer_thread_count >= 1)
+ /* The number of threads should be smaller than the number of
+ arenas, so that there will be some free arenas to add to the
+ arena free list. */
+ enum { outer_thread_count = 2 };
+ if (mallopt (M_ARENA_MAX, 8) == 0)
{
- /* See NARENAS_FROM_NCORES in malloc.c. */
- if (sizeof (long) == 4)
- outer_thread_count *= 2;
- else
- outer_thread_count *= 8;
+ printf ("error: mallopt (M_ARENA_MAX) failed\n");
+ return 1;
}
/* Leave some room for shutting down all threads gracefully. */
- int timeout = TIMEOUT - 2;
-
- pthread_t *threads = calloc (sizeof (*threads), outer_thread_count);
- if (threads == NULL)
- {
- printf ("error: calloc: %m\n");
- abort ();
- }
+ int timeout = 3;
+ if (timeout > DEFAULT_TIMEOUT)
+ timeout = DEFAULT_TIMEOUT - 1;
+ pthread_t *threads = xcalloc (sizeof (*threads), outer_thread_count);
for (long i = 0; i < outer_thread_count; ++i)
- {
- int ret = pthread_create (threads + i, NULL, outer_thread, NULL);
- if (ret != 0)
- {
- errno = ret;
- printf ("error: pthread_create: %m\n");
- abort ();
- }
- }
+ threads[i] = xpthread_create (NULL, outer_thread, NULL);
struct timespec ts = {timeout, 0};
if (nanosleep (&ts, NULL))
@@ -199,19 +128,10 @@ do_test (void)
__atomic_store_n (&termination_requested, true, __ATOMIC_RELAXED);
for (long i = 0; i < outer_thread_count; ++i)
- {
- int ret = pthread_join (threads[i], NULL);
- if (ret != 0)
- {
- errno = ret;
- printf ("error: pthread_join: %m\n");
- abort ();
- }
- }
+ xpthread_join (threads[i]);
free (threads);
return 0;
}
-#define TEST_FUNCTION do_test ()
-#include "../test-skeleton.c"
+#include <support/test-driver.c>
diff --git a/malloc/tst-malloc-thread-fail.c b/malloc/tst-malloc-thread-fail.c
index fc090efccd..ce2e8f2f3d 100644
--- a/malloc/tst-malloc-thread-fail.c
+++ b/malloc/tst-malloc-thread-fail.c
@@ -1,5 +1,5 @@
/* Test allocation function behavior on allocation failure.
- Copyright (C) 2015-2016 Free Software Foundation, Inc.
+ Copyright (C) 2015-2018 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
@@ -436,7 +436,7 @@ do_test (void)
}
/* The repeated allocations take some time on slow machines. */
-#define TIMEOUT 20
+#define TIMEOUT 100
#define TEST_FUNCTION do_test ()
#include "../test-skeleton.c"
diff --git a/malloc/tst-malloc-too-large.c b/malloc/tst-malloc-too-large.c
new file mode 100644
index 0000000000..10fb136528
--- /dev/null
+++ b/malloc/tst-malloc-too-large.c
@@ -0,0 +1,253 @@
+/* Test and verify that too-large memory allocations fail with ENOMEM.
+ Copyright (C) 2018 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/>. */
+
+/* Bug 22375 reported a regression in malloc where if after malloc'ing then
+ free'ing a small block of memory, malloc is then called with a really
+ large size argument (close to SIZE_MAX): instead of returning NULL and
+ setting errno to ENOMEM, malloc incorrectly returns the previously
+ allocated block instead. Bug 22343 reported a similar case where
+ posix_memalign incorrectly returns successfully when called with an with
+ a really large size argument.
+
+ Both of these were caused by integer overflows in the allocator when it
+ was trying to pad the requested size to allow for book-keeping or
+ alignment. This test guards against such bugs by repeatedly allocating
+ and freeing small blocks of memory then trying to allocate various block
+ sizes larger than the memory bus width of 64-bit targets, or almost
+ as large as SIZE_MAX on 32-bit targets supported by glibc. In each case,
+ it verifies that such impossibly large allocations correctly fail. */
+
+
+#include <stdlib.h>
+#include <malloc.h>
+#include <errno.h>
+#include <stdint.h>
+#include <sys/resource.h>
+#include <libc-diag.h>
+#include <support/check.h>
+#include <unistd.h>
+#include <sys/param.h>
+
+
+/* This function prepares for each 'too-large memory allocation' test by
+ performing a small successful malloc/free and resetting errno prior to
+ the actual test. */
+static void
+test_setup (void)
+{
+ void *volatile ptr = malloc (16);
+ TEST_VERIFY_EXIT (ptr != NULL);
+ free (ptr);
+ errno = 0;
+}
+
+
+/* This function tests each of:
+ - malloc (SIZE)
+ - realloc (PTR_FOR_REALLOC, SIZE)
+ - for various values of NMEMB:
+ - calloc (NMEMB, SIZE/NMEMB)
+ - calloc (SIZE/NMEMB, NMEMB)
+ - reallocarray (PTR_FOR_REALLOC, NMEMB, SIZE/NMEMB)
+ - reallocarray (PTR_FOR_REALLOC, SIZE/NMEMB, NMEMB)
+ and precedes each of these tests with a small malloc/free before it. */
+static void
+test_large_allocations (size_t size)
+{
+ void * ptr_to_realloc;
+
+ test_setup ();
+ TEST_VERIFY (malloc (size) == NULL);
+ TEST_VERIFY (errno == ENOMEM);
+
+ ptr_to_realloc = malloc (16);
+ TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
+ test_setup ();
+ TEST_VERIFY (realloc (ptr_to_realloc, size) == NULL);
+ TEST_VERIFY (errno == ENOMEM);
+ free (ptr_to_realloc);
+
+ for (size_t nmemb = 1; nmemb <= 8; nmemb *= 2)
+ if ((size % nmemb) == 0)
+ {
+ test_setup ();
+ TEST_VERIFY (calloc (nmemb, size / nmemb) == NULL);
+ TEST_VERIFY (errno == ENOMEM);
+
+ test_setup ();
+ TEST_VERIFY (calloc (size / nmemb, nmemb) == NULL);
+ TEST_VERIFY (errno == ENOMEM);
+
+ ptr_to_realloc = malloc (16);
+ TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
+ test_setup ();
+ TEST_VERIFY (reallocarray (ptr_to_realloc, nmemb, size / nmemb) == NULL);
+ TEST_VERIFY (errno == ENOMEM);
+ free (ptr_to_realloc);
+
+ ptr_to_realloc = malloc (16);
+ TEST_VERIFY_EXIT (ptr_to_realloc != NULL);
+ test_setup ();
+ TEST_VERIFY (reallocarray (ptr_to_realloc, size / nmemb, nmemb) == NULL);
+ TEST_VERIFY (errno == ENOMEM);
+ free (ptr_to_realloc);
+ }
+ else
+ break;
+}
+
+
+static long pagesize;
+
+/* This function tests the following aligned memory allocation functions
+ using several valid alignments and precedes each allocation test with a
+ small malloc/free before it:
+ memalign, posix_memalign, aligned_alloc, valloc, pvalloc. */
+static void
+test_large_aligned_allocations (size_t size)
+{
+ /* ptr stores the result of posix_memalign but since all those calls
+ should fail, posix_memalign should never change ptr. We set it to
+ NULL here and later on we check that it remains NULL after each
+ posix_memalign call. */
+ void * ptr = NULL;
+
+ size_t align;
+
+ /* All aligned memory allocation functions expect an alignment that is a
+ power of 2. Given this, we test each of them with every valid
+ alignment from 1 thru PAGESIZE. */
+ for (align = 1; align <= pagesize; align *= 2)
+ {
+ test_setup ();
+ TEST_VERIFY (memalign (align, size) == NULL);
+ TEST_VERIFY (errno == ENOMEM);
+
+ /* posix_memalign expects an alignment that is a power of 2 *and* a
+ multiple of sizeof (void *). */
+ if ((align % sizeof (void *)) == 0)
+ {
+ test_setup ();
+ TEST_VERIFY (posix_memalign (&ptr, align, size) == ENOMEM);
+ TEST_VERIFY (ptr == NULL);
+ }
+
+ /* aligned_alloc expects a size that is a multiple of alignment. */
+ if ((size % align) == 0)
+ {
+ test_setup ();
+ TEST_VERIFY (aligned_alloc (align, size) == NULL);
+ TEST_VERIFY (errno == ENOMEM);
+ }
+ }
+
+ /* Both valloc and pvalloc return page-aligned memory. */
+
+ test_setup ();
+ TEST_VERIFY (valloc (size) == NULL);
+ TEST_VERIFY (errno == ENOMEM);
+
+ test_setup ();
+ TEST_VERIFY (pvalloc (size) == NULL);
+ TEST_VERIFY (errno == ENOMEM);
+}
+
+
+#define FOURTEEN_ON_BITS ((1UL << 14) - 1)
+#define FIFTY_ON_BITS ((1UL << 50) - 1)
+
+
+static int
+do_test (void)
+{
+
+#if __WORDSIZE >= 64
+
+ /* This test assumes that none of the supported targets have an address
+ bus wider than 50 bits, and that therefore allocations for sizes wider
+ than 50 bits will fail. Here, we ensure that the assumption continues
+ to be true in the future when we might have address buses wider than 50
+ bits. */
+
+ struct rlimit alloc_size_limit
+ = {
+ .rlim_cur = FIFTY_ON_BITS,
+ .rlim_max = FIFTY_ON_BITS
+ };
+
+ setrlimit (RLIMIT_AS, &alloc_size_limit);
+
+#endif /* __WORDSIZE >= 64 */
+
+ DIAG_PUSH_NEEDS_COMMENT;
+#if __GNUC_PREREQ (7, 0)
+ /* GCC 7 warns about too-large allocations; here we want to test
+ that they fail. */
+ DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
+#endif
+
+ /* Aligned memory allocation functions need to be tested up to alignment
+ size equivalent to page size, which should be a power of 2. */
+ pagesize = sysconf (_SC_PAGESIZE);
+ TEST_VERIFY_EXIT (powerof2 (pagesize));
+
+ /* Loop 1: Ensure that all allocations with SIZE close to SIZE_MAX, i.e.
+ in the range (SIZE_MAX - 2^14, SIZE_MAX], fail.
+
+ We can expect that this range of allocation sizes will always lead to
+ an allocation failure on both 64 and 32 bit targets, because:
+
+ 1. no currently supported 64-bit target has an address bus wider than
+ 50 bits -- and (2^64 - 2^14) is much wider than that;
+
+ 2. on 32-bit targets, even though 2^32 is only 4 GB and potentially
+ addressable, glibc itself is more than 2^14 bytes in size, and
+ therefore once glibc is loaded, less than (2^32 - 2^14) bytes remain
+ available. */
+
+ for (size_t i = 0; i <= FOURTEEN_ON_BITS; i++)
+ {
+ test_large_allocations (SIZE_MAX - i);
+ test_large_aligned_allocations (SIZE_MAX - i);
+ }
+
+#if __WORDSIZE >= 64
+ /* On 64-bit targets, we need to test a much wider range of too-large
+ sizes, so we test at intervals of (1 << 50) that allocation sizes
+ ranging from SIZE_MAX down to (1 << 50) fail:
+ The 14 MSBs are decremented starting from "all ON" going down to 1,
+ the 50 LSBs are "all ON" and then "all OFF" during every iteration. */
+ for (size_t msbs = FOURTEEN_ON_BITS; msbs >= 1; msbs--)
+ {
+ size_t size = (msbs << 50) | FIFTY_ON_BITS;
+ test_large_allocations (size);
+ test_large_aligned_allocations (size);
+
+ size = msbs << 50;
+ test_large_allocations (size);
+ test_large_aligned_allocations (size);
+ }
+#endif /* __WORDSIZE >= 64 */
+
+ DIAG_POP_NEEDS_COMMENT;
+
+ return 0;
+}
+
+
+#include <support/test-driver.c>
diff --git a/malloc/tst-malloc-usable-static-tunables.c b/malloc/tst-malloc-usable-static-tunables.c
new file mode 100644
index 0000000000..8907db01a5
--- /dev/null
+++ b/malloc/tst-malloc-usable-static-tunables.c
@@ -0,0 +1 @@
+#include <malloc/tst-malloc-usable.c>
diff --git a/malloc/tst-malloc-usable-static.c b/malloc/tst-malloc-usable-static.c
new file mode 100644
index 0000000000..8907db01a5
--- /dev/null
+++ b/malloc/tst-malloc-usable-static.c
@@ -0,0 +1 @@
+#include <malloc/tst-malloc-usable.c>
diff --git a/malloc/tst-malloc-usable-tunables.c b/malloc/tst-malloc-usable-tunables.c
new file mode 100644
index 0000000000..8907db01a5
--- /dev/null
+++ b/malloc/tst-malloc-usable-tunables.c
@@ -0,0 +1 @@
+#include <malloc/tst-malloc-usable.c>
diff --git a/malloc/tst-malloc-usable.c b/malloc/tst-malloc-usable.c
index ca67ed73e0..d904c6b693 100644
--- a/malloc/tst-malloc-usable.c
+++ b/malloc/tst-malloc-usable.c
@@ -1,7 +1,7 @@
/* Ensure that malloc_usable_size returns the request size with
MALLOC_CHECK_ exported to a positive value.
- Copyright (C) 2012-2016 Free Software Foundation, Inc.
+ Copyright (C) 2012-2018 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
diff --git a/malloc/tst-malloc.c b/malloc/tst-malloc.c
index c1292a24f8..aa1aa2aa41 100644
--- a/malloc/tst-malloc.c
+++ b/malloc/tst-malloc.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1999-2016 Free Software Foundation, Inc.
+/* Copyright (C) 1999-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Andreas Jaeger <aj@arthur.rhein-neckar.de>, 1999.
@@ -19,6 +19,7 @@
#include <errno.h>
#include <malloc.h>
#include <stdio.h>
+#include <libc-diag.h>
static int errors = 0;
@@ -37,7 +38,14 @@ do_test (void)
errno = 0;
+ DIAG_PUSH_NEEDS_COMMENT;
+#if __GNUC_PREREQ (7, 0)
+ /* GCC 7 warns about too-large allocations; here we want to test
+ that they fail. */
+ DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
+#endif
p = malloc (-1);
+ DIAG_POP_NEEDS_COMMENT;
save = errno;
if (p != NULL)
@@ -67,7 +75,14 @@ do_test (void)
if (p == NULL)
merror ("malloc (513K) failed.");
+ DIAG_PUSH_NEEDS_COMMENT;
+#if __GNUC_PREREQ (7, 0)
+ /* GCC 7 warns about too-large allocations; here we want to test
+ that they fail. */
+ DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
+#endif
q = malloc (-512 * 1024);
+ DIAG_POP_NEEDS_COMMENT;
if (q != NULL)
merror ("malloc (-512K) succeeded.");
diff --git a/malloc/tst-malloc_info.c b/malloc/tst-malloc_info.c
new file mode 100644
index 0000000000..049d2a9b92
--- /dev/null
+++ b/malloc/tst-malloc_info.c
@@ -0,0 +1,101 @@
+/* Smoke test for malloc_info.
+ Copyright (C) 2017-2018 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/>. */
+
+/* The purpose of this test is to provide a quick way to run
+ malloc_info in a multi-threaded process. */
+
+#include <array_length.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <support/support.h>
+#include <support/xthread.h>
+
+/* This barrier is used to have the main thread wait until the helper
+ threads have performed their allocations. */
+static pthread_barrier_t barrier;
+
+enum
+ {
+ /* Number of threads performing allocations. */
+ thread_count = 4,
+
+ /* Amount of memory allocation per thread. This should be large
+ enough to cause the allocation of multiple heaps per arena. */
+ per_thread_allocations
+ = sizeof (void *) == 4 ? 16 * 1024 * 1024 : 128 * 1024 * 1024,
+ };
+
+static void *
+allocation_thread_function (void *closure)
+{
+ struct list
+ {
+ struct list *next;
+ long dummy[4];
+ };
+
+ struct list *head = NULL;
+ size_t allocated = 0;
+ while (allocated < per_thread_allocations)
+ {
+ struct list *new_head = xmalloc (sizeof (*new_head));
+ allocated += sizeof (*new_head);
+ new_head->next = head;
+ head = new_head;
+ }
+
+ xpthread_barrier_wait (&barrier);
+
+ /* Main thread prints first statistics here. */
+
+ xpthread_barrier_wait (&barrier);
+
+ while (head != NULL)
+ {
+ struct list *next_head = head->next;
+ free (head);
+ head = next_head;
+ }
+
+ return NULL;
+}
+
+static int
+do_test (void)
+{
+ xpthread_barrier_init (&barrier, NULL, thread_count + 1);
+
+ pthread_t threads[thread_count];
+ for (size_t i = 0; i < array_length (threads); ++i)
+ threads[i] = xpthread_create (NULL, allocation_thread_function, NULL);
+
+ xpthread_barrier_wait (&barrier);
+ puts ("info: After allocation:");
+ malloc_info (0, stdout);
+
+ xpthread_barrier_wait (&barrier);
+ for (size_t i = 0; i < array_length (threads); ++i)
+ xpthread_join (threads[i]);
+
+ puts ("\ninfo: After deallocation:");
+ malloc_info (0, stdout);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/malloc/tst-mallocfork.c b/malloc/tst-mallocfork.c
index f90ce94887..4ff6ec09f4 100644
--- a/malloc/tst-mallocfork.c
+++ b/malloc/tst-mallocfork.c
@@ -1,5 +1,5 @@
/* Derived from the test case in
- http://sourceware.org/bugzilla/show_bug.cgi?id=838. */
+ https://sourceware.org/bugzilla/show_bug.cgi?id=838. */
#include <assert.h>
#include <errno.h>
#include <stdio.h>
diff --git a/malloc/tst-mallocfork2.c b/malloc/tst-mallocfork2.c
new file mode 100644
index 0000000000..bf8393b75a
--- /dev/null
+++ b/malloc/tst-mallocfork2.c
@@ -0,0 +1,211 @@
+/* Test case for async-signal-safe fork (with respect to malloc).
+ Copyright (C) 2016-2018 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/>. */
+
+/* This test will fail if the process is multi-threaded because we
+ only have an async-signal-safe fork in the single-threaded case
+ (where we skip acquiring the malloc heap locks).
+
+ This test only checks async-signal-safety with regards to malloc;
+ other, more rarely-used glibc subsystems could have locks which
+ still make fork unsafe, even in single-threaded processes. */
+
+#include <errno.h>
+#include <sched.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+/* How many malloc objects to keep arond. */
+enum { malloc_objects = 1009 };
+
+/* The maximum size of an object. */
+enum { malloc_maximum_size = 70000 };
+
+/* How many signals need to be delivered before the test exits. */
+enum { signal_count = 1000 };
+
+static int do_test (void);
+#define TIMEOUT 100
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
+
+/* Process ID of the subprocess which sends SIGUSR1 signals. */
+static pid_t sigusr1_sender_pid;
+
+/* Set to 1 if SIGUSR1 is received. Used to detect a signal during
+ malloc/free. */
+static volatile sig_atomic_t sigusr1_received;
+
+/* Periodically set to 1, to indicate that the process is making
+ progress. Checked by liveness_signal_handler. */
+static volatile sig_atomic_t progress_indicator = 1;
+
+static void
+sigusr1_handler (int signo)
+{
+ /* Let the main program make progress, by temporarily suspending
+ signals from the subprocess. */
+ if (sigusr1_received)
+ return;
+ /* sigusr1_sender_pid might not be initialized in the parent when
+ the first SIGUSR1 signal arrives. */
+ if (sigusr1_sender_pid > 0 && kill (sigusr1_sender_pid, SIGSTOP) != 0)
+ {
+ write_message ("error: kill (SIGSTOP)\n");
+ abort ();
+ }
+ sigusr1_received = 1;
+
+ /* Perform a fork with a trivial subprocess. */
+ pid_t pid = fork ();
+ if (pid == -1)
+ {
+ write_message ("error: fork\n");
+ abort ();
+ }
+ if (pid == 0)
+ _exit (0);
+ int status;
+ int ret = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0));
+ if (ret < 0)
+ {
+ write_message ("error: waitpid\n");
+ abort ();
+ }
+ if (status != 0)
+ {
+ write_message ("error: unexpected exit status from subprocess\n");
+ abort ();
+ }
+}
+
+static void
+liveness_signal_handler (int signo)
+{
+ if (progress_indicator)
+ progress_indicator = 0;
+ else
+ write_message ("warning: process seems to be stuck\n");
+}
+
+static void
+__attribute__ ((noreturn))
+signal_sender (int signo, bool sleep)
+{
+ pid_t target = getppid ();
+ while (true)
+ {
+ if (kill (target, signo) != 0)
+ {
+ dprintf (STDOUT_FILENO, "error: kill: %m\n");
+ abort ();
+ }
+ if (sleep)
+ usleep (1 * 1000 * 1000);
+ else
+ /* Reduce the rate at which we send signals. */
+ sched_yield ();
+ }
+}
+
+static int
+do_test (void)
+{
+ struct sigaction action =
+ {
+ .sa_handler = sigusr1_handler,
+ };
+ sigemptyset (&action.sa_mask);
+
+ if (sigaction (SIGUSR1, &action, NULL) != 0)
+ {
+ printf ("error: sigaction: %m");
+ return 1;
+ }
+
+ action.sa_handler = liveness_signal_handler;
+ if (sigaction (SIGUSR2, &action, NULL) != 0)
+ {
+ printf ("error: sigaction: %m");
+ return 1;
+ }
+
+ pid_t sigusr2_sender_pid = fork ();
+ if (sigusr2_sender_pid == 0)
+ signal_sender (SIGUSR2, true);
+ sigusr1_sender_pid = fork ();
+ if (sigusr1_sender_pid == 0)
+ signal_sender (SIGUSR1, false);
+
+ void *objects[malloc_objects] = {};
+ unsigned signals = 0;
+ unsigned seed = 1;
+ time_t last_report = 0;
+ while (signals < signal_count)
+ {
+ progress_indicator = 1;
+ int slot = rand_r (&seed) % malloc_objects;
+ size_t size = rand_r (&seed) % malloc_maximum_size;
+ if (kill (sigusr1_sender_pid, SIGCONT) != 0)
+ {
+ printf ("error: kill (SIGCONT): %m\n");
+ signal (SIGUSR1, SIG_IGN);
+ kill (sigusr1_sender_pid, SIGKILL);
+ kill (sigusr2_sender_pid, SIGKILL);
+ return 1;
+ }
+ sigusr1_received = false;
+ free (objects[slot]);
+ objects[slot] = malloc (size);
+ if (sigusr1_received)
+ {
+ ++signals;
+ time_t current = time (0);
+ if (current != last_report)
+ {
+ printf ("info: SIGUSR1 signal count: %u\n", signals);
+ last_report = current;
+ }
+ }
+ if (objects[slot] == NULL)
+ {
+ printf ("error: malloc: %m\n");
+ signal (SIGUSR1, SIG_IGN);
+ kill (sigusr1_sender_pid, SIGKILL);
+ kill (sigusr2_sender_pid, SIGKILL);
+ return 1;
+ }
+ }
+
+ /* Clean up allocations. */
+ for (int slot = 0; slot < malloc_objects; ++slot)
+ free (objects[slot]);
+
+ /* Terminate the signal-sending subprocess. The SIGUSR1 handler
+ should no longer run because it uses sigusr1_sender_pid. */
+ signal (SIGUSR1, SIG_IGN);
+ kill (sigusr1_sender_pid, SIGKILL);
+ kill (sigusr2_sender_pid, SIGKILL);
+
+ return 0;
+}
diff --git a/malloc/tst-mallocstate.c b/malloc/tst-mallocstate.c
index a00d045985..e3d1ec157c 100644
--- a/malloc/tst-mallocstate.c
+++ b/malloc/tst-mallocstate.c
@@ -1,4 +1,5 @@
-/* Copyright (C) 2001-2016 Free Software Foundation, Inc.
+/* Emulate Emacs heap dumping to test malloc_set_state.
+ Copyright (C) 2001-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Wolfram Gloger <wg@malloc.de>, 2001.
@@ -17,68 +18,484 @@
<http://www.gnu.org/licenses/>. */
#include <errno.h>
+#include <stdbool.h>
#include <stdio.h>
+#include <string.h>
+#include <libc-symbols.h>
+#include <shlib-compat.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/test-driver.h>
+
#include "malloc.h"
-static int errors = 0;
+#if TEST_COMPAT (libc, GLIBC_2_0, GLIBC_2_25)
+
+/* Make the compatibility symbols availabile to this test case. */
+void *malloc_get_state (void);
+compat_symbol_reference (libc, malloc_get_state, malloc_get_state, GLIBC_2_0);
+int malloc_set_state (void *);
+compat_symbol_reference (libc, malloc_set_state, malloc_set_state, GLIBC_2_0);
+
+/* Maximum object size in the fake heap. */
+enum { max_size = 64 };
+
+/* Allocation actions. These are randomized actions executed on the
+ dumped heap (see allocation_tasks below). They are interspersed
+ with operations on the new heap (see heap_activity). */
+enum allocation_action
+ {
+ action_free, /* Dumped and freed. */
+ action_realloc, /* Dumped and realloc'ed. */
+ action_realloc_same, /* Dumped and realloc'ed, same size. */
+ action_realloc_smaller, /* Dumped and realloc'ed, shrinked. */
+ action_count
+ };
+
+/* Dumped heap. Initialize it, so that the object is placed into the
+ .data section, for increased realism. The size is an upper bound;
+ we use about half of the space. */
+static size_t dumped_heap[action_count * max_size * max_size
+ / sizeof (size_t)] = {1};
+
+/* Next free space in the dumped heap. Also top of the heap at the
+ end of the initialization procedure. */
+static size_t *next_heap_chunk;
+
+/* Copied from malloc.c and hooks.c. The version is deliberately
+ lower than the final version of malloc_set_state. */
+# define NBINS 128
+# define MALLOC_STATE_MAGIC 0x444c4541l
+# define MALLOC_STATE_VERSION (0 * 0x100l + 4l)
+static struct
+{
+ long magic;
+ long version;
+ void *av[NBINS * 2 + 2];
+ char *sbrk_base;
+ int sbrked_mem_bytes;
+ unsigned long trim_threshold;
+ unsigned long top_pad;
+ unsigned int n_mmaps_max;
+ unsigned long mmap_threshold;
+ int check_action;
+ unsigned long max_sbrked_mem;
+ unsigned long max_total_mem;
+ unsigned int n_mmaps;
+ unsigned int max_n_mmaps;
+ unsigned long mmapped_mem;
+ unsigned long max_mmapped_mem;
+ int using_malloc_checking;
+ unsigned long max_fast;
+ unsigned long arena_test;
+ unsigned long arena_max;
+ unsigned long narenas;
+} save_state =
+ {
+ .magic = MALLOC_STATE_MAGIC,
+ .version = MALLOC_STATE_VERSION,
+ };
+
+/* Allocate a blob in the fake heap. */
+static void *
+dumped_heap_alloc (size_t length)
+{
+ /* malloc needs three state bits in the size field, so the minimum
+ alignment is 8 even on 32-bit architectures. malloc_set_state
+ should be compatible with such heaps even if it currently
+ provides more alignment to applications. */
+ enum
+ {
+ heap_alignment = 8,
+ heap_alignment_mask = heap_alignment - 1
+ };
+ _Static_assert (sizeof (size_t) <= heap_alignment,
+ "size_t compatible with heap alignment");
+
+ /* Need at least this many bytes for metadata and application
+ data. */
+ size_t chunk_size = sizeof (size_t) + length;
+ /* Round up the allocation size to the heap alignment. */
+ chunk_size += heap_alignment_mask;
+ chunk_size &= ~heap_alignment_mask;
+ TEST_VERIFY_EXIT ((chunk_size & 3) == 0);
+ if (next_heap_chunk == NULL)
+ /* Initialize the top of the heap. Add one word of zero padding,
+ to match existing practice. */
+ {
+ dumped_heap[0] = 0;
+ next_heap_chunk = dumped_heap + 1;
+ }
+ else
+ /* The previous chunk is allocated. */
+ chunk_size |= 1;
+ *next_heap_chunk = chunk_size;
+
+ /* User data starts after the chunk header. */
+ void *result = next_heap_chunk + 1;
+ next_heap_chunk += chunk_size / sizeof (size_t);
+
+ /* Mark the previous chunk as used. */
+ *next_heap_chunk = 1;
+ return result;
+}
+
+/* Global seed variable for the random number generator. */
+static unsigned long long global_seed;
+
+/* Simple random number generator. The numbers are in the range from
+ 0 to UINT_MAX (inclusive). */
+static unsigned int
+rand_next (unsigned long long *seed)
+{
+ /* Linear congruential generated as used for MMIX. */
+ *seed = *seed * 6364136223846793005ULL + 1442695040888963407ULL;
+ return *seed >> 32;
+}
+
+/* Fill LENGTH bytes at BUFFER with random contents, as determined by
+ SEED. */
+static void
+randomize_buffer (unsigned char *buffer, size_t length,
+ unsigned long long seed)
+{
+ for (size_t i = 0; i < length; ++i)
+ buffer[i] = rand_next (&seed);
+}
+
+/* Dumps the buffer to standard output, in hexadecimal. */
+static void
+dump_hex (unsigned char *buffer, size_t length)
+{
+ for (int i = 0; i < length; ++i)
+ printf (" %02X", buffer[i]);
+}
+
+/* Set to true if an error is encountered. */
+static bool errors = false;
+
+/* Keep track of object allocations. */
+struct allocation
+{
+ unsigned char *data;
+ unsigned int size;
+ unsigned int seed;
+};
+
+/* Check that the allocation task allocation has the expected
+ contents. */
+static void
+check_allocation (const struct allocation *alloc, int index)
+{
+ size_t size = alloc->size;
+ if (alloc->data == NULL)
+ {
+ printf ("error: NULL pointer for allocation of size %zu at %d, seed %u\n",
+ size, index, alloc->seed);
+ errors = true;
+ return;
+ }
+
+ unsigned char expected[4096];
+ if (size > sizeof (expected))
+ {
+ printf ("error: invalid allocation size %zu at %d, seed %u\n",
+ size, index, alloc->seed);
+ errors = true;
+ return;
+ }
+ randomize_buffer (expected, size, alloc->seed);
+ if (memcmp (alloc->data, expected, size) != 0)
+ {
+ printf ("error: allocation %d data mismatch, size %zu, seed %u\n",
+ index, size, alloc->seed);
+ printf (" expected:");
+ dump_hex (expected, size);
+ putc ('\n', stdout);
+ printf (" actual:");
+ dump_hex (alloc->data, size);
+ putc ('\n', stdout);
+ errors = true;
+ }
+}
+
+/* A heap allocation combined with pending actions on it. */
+struct allocation_task
+{
+ struct allocation allocation;
+ enum allocation_action action;
+};
+
+/* Allocation tasks. Initialized by init_allocation_tasks and used by
+ perform_allocations. */
+enum { allocation_task_count = action_count * max_size };
+static struct allocation_task allocation_tasks[allocation_task_count];
+
+/* Fisher-Yates shuffle of allocation_tasks. */
+static void
+shuffle_allocation_tasks (void)
+{
+ for (int i = 0; i < allocation_task_count - 1; ++i)
+ {
+ /* Pick pair in the tail of the array. */
+ int j = i + (rand_next (&global_seed)
+ % ((unsigned) (allocation_task_count - i)));
+ TEST_VERIFY_EXIT (j >= 0 && j < allocation_task_count);
+ /* Exchange. */
+ struct allocation_task tmp = allocation_tasks[i];
+ allocation_tasks[i] = allocation_tasks[j];
+ allocation_tasks[j] = tmp;
+ }
+}
+
+/* Set up the allocation tasks and the dumped heap. */
+static void
+initial_allocations (void)
+{
+ /* Initialize in a position-dependent way. */
+ for (int i = 0; i < allocation_task_count; ++i)
+ allocation_tasks[i] = (struct allocation_task)
+ {
+ .allocation =
+ {
+ .size = 1 + (i / action_count),
+ .seed = i,
+ },
+ .action = i % action_count
+ };
+
+ /* Execute the tasks in a random order. */
+ shuffle_allocation_tasks ();
+
+ /* Initialize the contents of the dumped heap. */
+ for (int i = 0; i < allocation_task_count; ++i)
+ {
+ struct allocation_task *task = allocation_tasks + i;
+ task->allocation.data = dumped_heap_alloc (task->allocation.size);
+ randomize_buffer (task->allocation.data, task->allocation.size,
+ task->allocation.seed);
+ }
+
+ for (int i = 0; i < allocation_task_count; ++i)
+ check_allocation (&allocation_tasks[i].allocation, i);
+}
+
+/* Indicates whether init_heap has run. This variable needs to be
+ volatile because malloc is declared __THROW, which implies it is a
+ leaf function, but we expect it to run our hooks. */
+static volatile bool heap_initialized;
+
+/* Executed by glibc malloc, through __malloc_initialize_hook
+ below. */
+static void
+init_heap (void)
+{
+ if (test_verbose)
+ printf ("info: performing heap initialization\n");
+ heap_initialized = true;
+
+ /* Populate the dumped heap. */
+ initial_allocations ();
+
+ /* Complete initialization of the saved heap data structure. */
+ save_state.sbrk_base = (void *) dumped_heap;
+ save_state.sbrked_mem_bytes = sizeof (dumped_heap);
+ /* Top pointer. Adjust so that it points to the start of struct
+ malloc_chunk. */
+ save_state.av[2] = (void *) (next_heap_chunk - 1);
+
+ /* Integrate the dumped heap into the process heap. */
+ TEST_VERIFY_EXIT (malloc_set_state (&save_state) == 0);
+}
+
+/* Interpose the initialization callback. */
+void (*volatile __malloc_initialize_hook) (void) = init_heap;
+
+/* Simulate occasional unrelated heap activity in the non-dumped
+ heap. */
+enum { heap_activity_allocations_count = 32 };
+static struct allocation heap_activity_allocations
+ [heap_activity_allocations_count] = {};
+static int heap_activity_seed_counter = 1000 * 1000;
+
+static void
+heap_activity (void)
+{
+ /* Only do this from time to time. */
+ if ((rand_next (&global_seed) % 4) == 0)
+ {
+ int slot = rand_next (&global_seed) % heap_activity_allocations_count;
+ struct allocation *alloc = heap_activity_allocations + slot;
+ if (alloc->data == NULL)
+ {
+ alloc->size = rand_next (&global_seed) % (4096U + 1);
+ alloc->data = xmalloc (alloc->size);
+ alloc->seed = heap_activity_seed_counter++;
+ randomize_buffer (alloc->data, alloc->size, alloc->seed);
+ check_allocation (alloc, 1000 + slot);
+ }
+ else
+ {
+ check_allocation (alloc, 1000 + slot);
+ free (alloc->data);
+ alloc->data = NULL;
+ }
+ }
+}
+
+static void
+heap_activity_deallocate (void)
+{
+ for (int i = 0; i < heap_activity_allocations_count; ++i)
+ free (heap_activity_allocations[i].data);
+}
+
+/* Perform a full heap check across the dumped heap allocation tasks,
+ and the simulated heap activity directly above. */
+static void
+full_heap_check (void)
+{
+ /* Dumped heap. */
+ for (int i = 0; i < allocation_task_count; ++i)
+ if (allocation_tasks[i].allocation.data != NULL)
+ check_allocation (&allocation_tasks[i].allocation, i);
+
+ /* Heap activity allocations. */
+ for (int i = 0; i < heap_activity_allocations_count; ++i)
+ if (heap_activity_allocations[i].data != NULL)
+ check_allocation (heap_activity_allocations + i, i);
+}
+/* Used as an optimization barrier to force a heap allocation. */
+__attribute__ ((noinline, noclone))
static void
-merror (const char *msg)
+my_free (void *ptr)
{
- ++errors;
- printf ("Error: %s\n", msg);
+ free (ptr);
}
static int
do_test (void)
{
- void *p1, *p2;
- void *save_state;
- long i;
+ my_free (malloc (1));
+ TEST_VERIFY_EXIT (heap_initialized);
- errno = 0;
+ /* The first pass performs the randomly generated allocation
+ tasks. */
+ if (test_verbose)
+ printf ("info: first pass through allocation tasks\n");
+ full_heap_check ();
+
+ /* Execute the post-undump tasks in a random order. */
+ shuffle_allocation_tasks ();
- p1 = malloc (10);
- if (p1 == NULL)
- merror ("malloc (10) failed.");
+ for (int i = 0; i < allocation_task_count; ++i)
+ {
+ heap_activity ();
+ struct allocation_task *task = allocation_tasks + i;
+ switch (task->action)
+ {
+ case action_free:
+ check_allocation (&task->allocation, i);
+ free (task->allocation.data);
+ task->allocation.data = NULL;
+ break;
- p2 = malloc (20);
- if (p2 == NULL)
- merror ("malloc (20) failed.");
+ case action_realloc:
+ check_allocation (&task->allocation, i);
+ task->allocation.data = xrealloc
+ (task->allocation.data, task->allocation.size + max_size);
+ check_allocation (&task->allocation, i);
+ break;
+
+ case action_realloc_same:
+ check_allocation (&task->allocation, i);
+ task->allocation.data = xrealloc
+ (task->allocation.data, task->allocation.size);
+ check_allocation (&task->allocation, i);
+ break;
+
+ case action_realloc_smaller:
+ check_allocation (&task->allocation, i);
+ size_t new_size = task->allocation.size - 1;
+ task->allocation.data = xrealloc (task->allocation.data, new_size);
+ if (new_size == 0)
+ {
+ if (task->allocation.data != NULL)
+ {
+ printf ("error: realloc with size zero did not deallocate\n");
+ errors = true;
+ }
+ /* No further action on this task. */
+ task->action = action_free;
+ }
+ else
+ {
+ task->allocation.size = new_size;
+ check_allocation (&task->allocation, i);
+ }
+ break;
+
+ case action_count:
+ FAIL_EXIT1 ("task->action should never be action_count");
+ }
+ full_heap_check ();
+ }
- free (malloc (10));
+ /* The second pass frees the objects which were allocated during the
+ first pass. */
+ if (test_verbose)
+ printf ("info: second pass through allocation tasks\n");
- for (i = 0; i < 100; ++i)
+ shuffle_allocation_tasks ();
+ for (int i = 0; i < allocation_task_count; ++i)
{
- save_state = malloc_get_state ();
- if (save_state == NULL)
+ heap_activity ();
+ struct allocation_task *task = allocation_tasks + i;
+ switch (task->action)
{
- merror ("malloc_get_state () failed.");
+ case action_free:
+ /* Already freed, nothing to do. */
break;
+
+ case action_realloc:
+ case action_realloc_same:
+ case action_realloc_smaller:
+ check_allocation (&task->allocation, i);
+ free (task->allocation.data);
+ task->allocation.data = NULL;
+ break;
+
+ case action_count:
+ FAIL_EXIT1 ("task->action should never be action_count");
}
- /*free (malloc (10)); This could change the top chunk! */
- malloc_set_state (save_state);
- p1 = realloc (p1, i * 4 + 4);
- if (p1 == NULL)
- merror ("realloc (i*4) failed.");
- free (save_state);
+ full_heap_check ();
}
- p1 = realloc (p1, 40);
- free (p2);
- p2 = malloc (10);
- if (p2 == NULL)
- merror ("malloc (10) failed.");
- free (p1);
+ heap_activity_deallocate ();
- return errors != 0;
-}
+ /* Check that the malloc_get_state stub behaves in the intended
+ way. */
+ errno = 0;
+ if (malloc_get_state () != NULL)
+ {
+ printf ("error: malloc_get_state succeeded\n");
+ errors = true;
+ }
+ if (errno != ENOSYS)
+ {
+ printf ("error: malloc_get_state: %m\n");
+ errors = true;
+ }
-/*
- * Local variables:
- * c-basic-offset: 2
- * End:
- */
+ return errors;
+}
+#else
+static int
+do_test (void)
+{
+ return 77;
+}
+#endif
-#define TEST_FUNCTION do_test ()
-#include "../test-skeleton.c"
+#include <support/test-driver.c>
diff --git a/malloc/tst-mallopt.c b/malloc/tst-mallopt.c
index 390fba3391..68fc7662ff 100644
--- a/malloc/tst-mallopt.c
+++ b/malloc/tst-mallopt.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2014-2016 Free Software Foundation, Inc.
+/* Copyright (C) 2014-2018 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
diff --git a/malloc/tst-mcheck.c b/malloc/tst-mcheck.c
index 296cc473e8..18b0d59bd9 100644
--- a/malloc/tst-mcheck.c
+++ b/malloc/tst-mcheck.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2005-2016 Free Software Foundation, Inc.
+/* Copyright (C) 2005-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Jakub Jelinek <jakub@redhat.com>, 2005.
@@ -19,6 +19,7 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
+#include <libc-diag.h>
static int errors = 0;
@@ -36,7 +37,14 @@ do_test (void)
errno = 0;
+ DIAG_PUSH_NEEDS_COMMENT;
+#if __GNUC_PREREQ (7, 0)
+ /* GCC 7 warns about too-large allocations; here we want to test
+ that they fail. */
+ DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
+#endif
p = malloc (-1);
+ DIAG_POP_NEEDS_COMMENT;
if (p != NULL)
merror ("malloc (-1) succeeded.");
@@ -67,10 +75,17 @@ do_test (void)
if (p == NULL)
merror ("malloc (512) failed.");
+ DIAG_PUSH_NEEDS_COMMENT;
+#if __GNUC_PREREQ (7, 0)
+ /* GCC 7 warns about too-large allocations; here we want to test
+ that they fail. */
+ DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
+#endif
if (realloc (p, -256) != NULL)
merror ("realloc (p, -256) succeeded.");
else if (errno != ENOMEM)
merror ("errno is not set correctly.");
+ DIAG_POP_NEEDS_COMMENT;
free (p);
@@ -78,10 +93,17 @@ do_test (void)
if (p == NULL)
merror ("malloc (512) failed.");
+ DIAG_PUSH_NEEDS_COMMENT;
+#if __GNUC_PREREQ (7, 0)
+ /* GCC 7 warns about too-large allocations; here we want to test
+ that they fail. */
+ DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
+#endif
if (realloc (p, -1) != NULL)
merror ("realloc (p, -1) succeeded.");
else if (errno != ENOMEM)
merror ("errno is not set correctly.");
+ DIAG_POP_NEEDS_COMMENT;
free (p);
free (q);
diff --git a/malloc/tst-memalign.c b/malloc/tst-memalign.c
index 6650610143..904bf9491d 100644
--- a/malloc/tst-memalign.c
+++ b/malloc/tst-memalign.c
@@ -1,5 +1,5 @@
/* Test for memalign.
- Copyright (C) 2013-2016 Free Software Foundation, Inc.
+ Copyright (C) 2013-2018 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
diff --git a/malloc/tst-mtrace.c b/malloc/tst-mtrace.c
index fc44226087..927b8e7709 100644
--- a/malloc/tst-mtrace.c
+++ b/malloc/tst-mtrace.c
@@ -1,5 +1,5 @@
/* Test program for mtrace.
- Copyright (C) 2000-2016 Free Software Foundation, Inc.
+ Copyright (C) 2000-2018 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
diff --git a/malloc/tst-mtrace.sh b/malloc/tst-mtrace.sh
index cbcaea8122..9c50c0675d 100755
--- a/malloc/tst-mtrace.sh
+++ b/malloc/tst-mtrace.sh
@@ -1,6 +1,6 @@
#!/bin/sh
# Testing the mtrace function.
-# Copyright (C) 2000-2016 Free Software Foundation, Inc.
+# Copyright (C) 2000-2018 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
diff --git a/malloc/tst-posix_memalign.c b/malloc/tst-posix_memalign.c
index 054559994f..81b4c05ef4 100644
--- a/malloc/tst-posix_memalign.c
+++ b/malloc/tst-posix_memalign.c
@@ -1,5 +1,5 @@
/* Test for posix_memalign.
- Copyright (C) 2013-2016 Free Software Foundation, Inc.
+ Copyright (C) 2013-2018 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
diff --git a/malloc/tst-pvalloc.c b/malloc/tst-pvalloc.c
index ffce702159..aa391447ee 100644
--- a/malloc/tst-pvalloc.c
+++ b/malloc/tst-pvalloc.c
@@ -1,5 +1,5 @@
/* Test for pvalloc.
- Copyright (C) 2013-2016 Free Software Foundation, Inc.
+ Copyright (C) 2013-2018 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
diff --git a/malloc/tst-realloc.c b/malloc/tst-realloc.c
index 16e840facf..1e48ad494c 100644
--- a/malloc/tst-realloc.c
+++ b/malloc/tst-realloc.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2013-2016 Free Software Foundation, Inc.
+/* Copyright (C) 2013-2018 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
@@ -19,6 +19,7 @@
#include <malloc.h>
#include <stdio.h>
#include <string.h>
+#include <libc-diag.h>
static int errors = 0;
@@ -39,7 +40,14 @@ do_test (void)
errno = 0;
/* realloc (NULL, ...) behaves similarly to malloc (C89). */
+ DIAG_PUSH_NEEDS_COMMENT;
+#if __GNUC_PREREQ (7, 0)
+ /* GCC 7 warns about too-large allocations; here we want to test
+ that they fail. */
+ DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
+#endif
p = realloc (NULL, -1);
+ DIAG_POP_NEEDS_COMMENT;
save = errno;
if (p != NULL)
@@ -58,10 +66,6 @@ do_test (void)
if (p == NULL)
merror ("realloc (NULL, 10) failed.");
- /* errno should be clear on success (POSIX). */
- if (p != NULL && save != 0)
- merror ("errno is set but should not be");
-
free (p);
p = calloc (20, 1);
@@ -111,7 +115,14 @@ do_test (void)
merror ("first 16 bytes were not correct");
/* Check failed realloc leaves original untouched (C89). */
+ DIAG_PUSH_NEEDS_COMMENT;
+#if __GNUC_PREREQ (7, 0)
+ /* GCC 7 warns about too-large allocations; here we want to test
+ that they fail. */
+ DIAG_IGNORE_NEEDS_COMMENT (7, "-Walloc-size-larger-than=");
+#endif
c = realloc (p, -1);
+ DIAG_POP_NEEDS_COMMENT;
if (c != NULL)
merror ("realloc (p, -1) succeeded.");
diff --git a/malloc/tst-reallocarray.c b/malloc/tst-reallocarray.c
new file mode 100644
index 0000000000..b0d80ebc58
--- /dev/null
+++ b/malloc/tst-reallocarray.c
@@ -0,0 +1,118 @@
+/* Test for reallocarray.
+ Copyright (C) 2017-2018 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/>. */
+
+#include <errno.h>
+#include <malloc.h>
+#include <string.h>
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+ void *ptr = NULL;
+ void *ptr2 = NULL;
+ unsigned char *c;
+ size_t i;
+ int ok;
+ const size_t max = ~(size_t)0;
+ size_t a, b;
+
+ /* Test overflow detection. */
+ errno = 0;
+ ptr = reallocarray (NULL, max, 2);
+ TEST_VERIFY (!ptr);
+ TEST_VERIFY (errno == ENOMEM);
+
+ errno = 0;
+ ptr = reallocarray (NULL, 2, max);
+ TEST_VERIFY (!ptr);
+ TEST_VERIFY (errno == ENOMEM);
+
+ a = 65537;
+ b = max/65537 + 1;
+ errno = 0;
+ ptr = reallocarray (NULL, a, b);
+ TEST_VERIFY (!ptr);
+ TEST_VERIFY (errno == ENOMEM);
+
+ errno = 0;
+ ptr = reallocarray (NULL, b, a);
+ TEST_VERIFY (!ptr);
+ TEST_VERIFY (errno == ENOMEM);
+
+ /* Test realloc-like behavior. */
+ /* Allocate memory like malloc. */
+ ptr = reallocarray (NULL, 10, 2);
+ TEST_VERIFY_EXIT (ptr);
+ TEST_VERIFY_EXIT (malloc_usable_size (ptr) >= 10*2);
+
+ memset (ptr, 0xAF, 10*2);
+
+ /* Enlarge buffer. */
+ ptr2 = reallocarray (ptr, 20, 2);
+ TEST_VERIFY (ptr2);
+ if (ptr2)
+ ptr = ptr2;
+ TEST_VERIFY (malloc_usable_size (ptr) >= 20*2);
+
+ c = ptr;
+ ok = 1;
+ for (i = 0; i < 10*2; ++i)
+ {
+ if (c[i] != 0xAF)
+ ok = 0;
+ }
+ TEST_VERIFY (ok);
+
+ /* Decrease buffer size. */
+ ptr2 = reallocarray (ptr, 5, 3);
+ TEST_VERIFY (ptr2);
+ if (ptr2)
+ ptr = ptr2;
+ TEST_VERIFY_EXIT (malloc_usable_size (ptr) >= 5*3);
+
+ c = ptr;
+ ok = 1;
+ for (i = 0; i < 5*3; ++i)
+ {
+ if (c[i] != 0xAF)
+ ok = 0;
+ }
+ TEST_VERIFY (ok);
+
+ /* Overflow should leave buffer untouched. */
+ errno = 0;
+ ptr2 = reallocarray (ptr, 2, ~(size_t)0);
+ TEST_VERIFY (!ptr2);
+ TEST_VERIFY (errno == ENOMEM);
+
+ c = ptr;
+ ok = 1;
+ for (i = 0; i < 5*3; ++i)
+ {
+ if (c[i] != 0xAF)
+ ok = 0;
+ }
+ TEST_VERIFY (ok);
+
+ free (ptr);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/malloc/tst-scratch_buffer.c b/malloc/tst-scratch_buffer.c
index 073a6db435..59310dd811 100644
--- a/malloc/tst-scratch_buffer.c
+++ b/malloc/tst-scratch_buffer.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2015-2016 Free Software Foundation, Inc.
+ Copyright (C) 2015-2018 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
@@ -59,7 +59,7 @@ array_size_must_fail (size_t a, size_t b)
pass, a, b);
return false;
}
- if (buf.data != buf.__space)
+ if (buf.data != buf.__space.__c)
{
printf ("scratch_buffer_set_array_size did not free: %d %zu %zu\n",
pass, a, b);
diff --git a/malloc/tst-valloc.c b/malloc/tst-valloc.c
index afcb1e540d..ba57d9559a 100644
--- a/malloc/tst-valloc.c
+++ b/malloc/tst-valloc.c
@@ -1,5 +1,5 @@
/* Test for valloc.
- Copyright (C) 2013-2016 Free Software Foundation, Inc.
+ Copyright (C) 2013-2018 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