aboutsummaryrefslogtreecommitdiffstats
path: root/libtest
diff options
context:
space:
mode:
Diffstat (limited to 'libtest')
-rw-r--r--libtest/Makefile6
-rw-r--r--libtest/alloc.c335
-rw-r--r--libtest/alloc_have_custom.c15
-rw-r--r--libtest/common-config.mk16
-rw-r--r--libtest/common.h63
-rw-r--r--libtest/config.mk1
-rw-r--r--libtest/config_backtraces=false.mk10
-rw-r--r--libtest/config_backtraces=true.mk10
-rw-r--r--libtest/globals.c6
-rw-r--r--libtest/libtest.h387
-rw-r--r--libtest/libtest_alloc.c17
-rw-r--r--libtest/libtest_check_no_leaks.c2
-rw-r--r--libtest/libtest_dump_stack.c5
-rw-r--r--libtest/libtest_free.c4
-rw-r--r--libtest/libtest_get_alloc_failure_in.c31
-rw-r--r--libtest/libtest_print_backtrace.c24
-rw-r--r--libtest/libtest_set_alloc_failure_in.c32
-rw-r--r--libtest/libtest_stack_on_signal.c102
-rw-r--r--libtest/mmap.c72
19 files changed, 1091 insertions, 47 deletions
diff --git a/libtest/Makefile b/libtest/Makefile
index 1bb870b..86b30ed 100644
--- a/libtest/Makefile
+++ b/libtest/Makefile
@@ -11,6 +11,7 @@ include ../mk/$(OS).mk
OBJ =\
+ $(OBJ_MMAP)\
alloc.o\
alloc_have_custom.o\
globals.o\
@@ -24,7 +25,10 @@ OBJ =\
libtest_start_tracking.o\
libtest_stop_tracking.o\
libtest_dump_stack.o\
- $(OBJ_BACKTRACE)
+ libtest_stack_on_signal.o\
+ libtest_get_alloc_failure_in.o\
+ libtest_set_alloc_failure_in.o\
+ $(OBJ_BACKTRACE)\
HDR =\
libtest.h\
diff --git a/libtest/alloc.c b/libtest/alloc.c
index 842f856..65eda04 100644
--- a/libtest/alloc.c
+++ b/libtest/alloc.c
@@ -75,6 +75,7 @@ void *
{
libtest_malloc_is_custom = 1;
assert(libtest_have_custom_free());
+ assert(libtest_have_custom_free_sized());
return common_malloc(n, FROM_MALLOC);
}
@@ -87,6 +88,7 @@ void *
libtest_calloc_is_custom = 1;
assert(libtest_have_custom_free());
+ assert(libtest_have_custom_free_sized());
assert(n);
assert(m);
@@ -112,6 +114,7 @@ void *
{
libtest_realloc_is_custom = 1;
assert(libtest_have_custom_free());
+ assert(libtest_have_custom_free_sized());
return common_realloc(old_ptr, new_n, FROM_REALLOC);
}
@@ -122,6 +125,7 @@ void *
{
libtest_reallocarray_is_custom = 1;
assert(libtest_have_custom_free());
+ assert(libtest_have_custom_free_sized());
assert(new_n);
assert(new_m);
@@ -182,6 +186,7 @@ void *
{
libtest_memalign_is_custom = 1;
assert(libtest_have_custom_free());
+ assert(libtest_have_custom_free_aligned_sized());
return common_memalign(alignment, size, FROM_MEMALIGN);
}
@@ -192,6 +197,7 @@ void *
{
libtest_aligned_alloc_is_custom = 1;
assert(libtest_have_custom_free());
+ assert(libtest_have_custom_free_aligned_sized());
assert(alignment);
assert(!(size % alignment));
@@ -209,6 +215,7 @@ int
libtest_posix_memalign_is_custom = 1;
assert(libtest_have_custom_free());
+ assert(libtest_have_custom_free_aligned_sized());
assert(size);
assert(alignment);
@@ -232,8 +239,7 @@ int
}
-PURE
-size_t
+PURE size_t
(malloc_usable_size)(void *ptr)
{
libtest_malloc_usable_size_is_custom = 1;
@@ -248,6 +254,21 @@ void
{
libtest_free_is_custom = 1;
+ assert(libtest_have_custom_malloc());
+ assert(libtest_have_custom_calloc());
+ assert(libtest_have_custom_realloc());
+ assert(libtest_have_custom_reallocarray());
+ assert(libtest_have_custom_valloc());
+ assert(libtest_have_custom_pvalloc());
+ assert(libtest_have_custom_memalign());
+ assert(libtest_have_custom_aligned_alloc());
+ assert(libtest_have_custom_posix_memalign());
+ assert(libtest_have_custom_strdup());
+ assert(libtest_have_custom_strndup());
+ assert(libtest_have_custom_wcsdup());
+ assert(libtest_have_custom_wcsdup());
+ assert(libtest_have_custom_memdup());
+
if (ptr) {
struct meminfo *meminfo;
meminfo = GET_MEMINFO(ptr);
@@ -266,6 +287,12 @@ void
{
libtest_free_sized_is_custom = 1;
+ assert(libtest_have_custom_malloc());
+ assert(libtest_have_custom_calloc());
+ assert(libtest_have_custom_realloc());
+ assert(libtest_have_custom_reallocarray());
+ assert(libtest_have_custom_memdup());
+
if (ptr) {
struct meminfo *meminfo;
meminfo = GET_MEMINFO(ptr);
@@ -282,18 +309,113 @@ void
{
libtest_free_aligned_sized_is_custom = 1;
+ assert(libtest_have_custom_memalign());
+ assert(libtest_have_custom_aligned_alloc());
+ assert(libtest_have_custom_posix_memalign());
+
if (ptr) {
struct meminfo *meminfo;
meminfo = GET_MEMINFO(ptr);
assert(meminfo->alignment_type == CUSTOM_ALIGNMENT);
assert(meminfo->requested_alloc_size == size);
assert(meminfo->requested_alignment == alignment);
+ assert(meminfo->origin != FROM_STRDUP);
+ assert(meminfo->origin != FROM_STRNDUP);
+ assert(meminfo->origin != FROM_WCSDUP);
+ assert(meminfo->origin != FROM_WCSDUP);
}
libtest_free(ptr, REQUIRE_ZEROED);
}
+char *
+(strdup)(const char *s)
+{
+ size_t n;
+ char *p;
+
+ libtest_strdup_is_custom = 1;
+ assert(libtest_have_custom_free());
+
+ n = strlen(s) + 1u;
+ p = common_memalign(_Alignof(char), n * sizeof(*s), FROM_STRDUP);
+ if (p)
+ memcpy(p, s, n * sizeof(*s));
+ return p;
+}
+
+
+char *
+(strndup)(const char *s, size_t n)
+{
+ char *p;
+
+ libtest_strndup_is_custom = 1;
+ assert(libtest_have_custom_free());
+
+ n = strnlen(s, n);
+ p = common_memalign(_Alignof(char), (n + 1u) * sizeof(*s), FROM_STRNDUP);
+ if (p) {
+ memcpy(p, s, n * sizeof(*s));
+ p[n] = '\0';
+ }
+ return p;
+}
+
+
+wchar_t *
+(wcsdup)(const wchar_t *s)
+{
+ size_t n;
+ wchar_t *p;
+
+ libtest_wcsdup_is_custom = 1;
+ assert(libtest_have_custom_free());
+
+ n = wcslen(s) + 1u;
+ p = common_memalign(_Alignof(wchar_t), n * sizeof(*s), FROM_WCSDUP);
+ if (p)
+ memcpy(p, s, n * sizeof(*s));
+ return p;
+}
+
+
+wchar_t *
+(wcsndup)(const wchar_t *s, size_t n)
+{
+ wchar_t *p;
+
+ libtest_wcsndup_is_custom = 1;
+ assert(libtest_have_custom_free());
+
+ n = wcsnlen(s, n);
+ p = common_memalign(_Alignof(wchar_t), (n + 1u) * sizeof(*s), FROM_WCSNDUP);
+ if (p) {
+ memcpy(p, s, n * sizeof(*s));
+ p[n] = 0;
+ }
+ return p;
+}
+
+
+void *
+(memdup)(const void *s, size_t n)
+{
+ char *p;
+
+ libtest_memdup_is_custom = 1;
+ assert(libtest_have_custom_free());
+ assert(libtest_have_custom_free_sized());
+
+ p = common_malloc(n, FROM_MEMDUP);
+ if (p)
+ memcpy(p, s, n);
+ return p;
+}
+
+
+
#else
@@ -315,6 +437,11 @@ size_t (malloc_usable_size)(void *ptr);
void (free)(void *ptr);
void (free_sized)(void *ptr, size_t size);
void (free_aligned_sized)(void *ptr, size_t alignment, size_t size);
+char *(strdup)(const char *s);
+char *(strndup)(const char *s, size_t n);
+wchar_t *(wcsdup)(const wchar_t *s);
+wchar_t *(wcsndup)(const wchar_t *s, size_t n);
+void *(memdup)(const void *s, size_t n);
static void
@@ -463,14 +590,12 @@ check(int use_free)
}
-int
-main(void)
+static void
+check_successfuls(void)
{
size_t pagesize;
-
- SET_UP_ALARM();
-
- libtest_start_tracking();
+ char *s;
+ wchar_t *w;
check(1);
check(0);
@@ -495,10 +620,198 @@ main(void)
assert(malloc_usable_size(p) >= pagesize);
/* cannot be free(3)ed */
- assert(libtest_check_no_leaks());
- return 0;
+ libtest_start_tracking();
+
+ s = strdup("test string");
+ assert(s);
+ assert(malloc_usable_size(s) >= sizeof("test string"));
+ assert(GET_MEMINFO(s)->requested_alignment == _Alignof(char));
+ assert((uintptr_t)s % (uintptr_t)_Alignof(char) == 0u);
+ assert(GET_MEMINFO(s)->requested_alloc_size == sizeof("test string"));
+ EXPECT(!strcmp(s, "test string"));
+ free(s);
+
+ s = strndup("test string", 100u);
+ assert(s);
+ assert(malloc_usable_size(s) >= sizeof("test string"));
+ assert(GET_MEMINFO(s)->requested_alignment == _Alignof(char));
+ assert((uintptr_t)s % (uintptr_t)_Alignof(char) == 0u);
+ assert(GET_MEMINFO(s)->requested_alloc_size == sizeof("test string"));
+ EXPECT(!strcmp(s, "test string"));
+ free(s);
+
+ s = strndup("test string", 4u);
+ assert(s);
+ assert(malloc_usable_size(s) >= sizeof("test"));
+ assert(GET_MEMINFO(s)->requested_alignment == _Alignof(char));
+ assert((uintptr_t)s % (uintptr_t)_Alignof(char) == 0u);
+ assert(GET_MEMINFO(s)->requested_alloc_size == sizeof("test"));
+ EXPECT(!strcmp(s, "test"));
+ free(s);
+
+ s = memdup("test", 4u);
+ assert(s);
+ assert(malloc_usable_size(s) >= 4u);
+ assert(GET_MEMINFO(s)->requested_alignment == _Alignof(max_align_t));
+ assert((uintptr_t)s % (uintptr_t)_Alignof(max_align_t) == 0u);
+ assert(GET_MEMINFO(s)->requested_alloc_size == 4u);
+ EXPECT(!memcmp(s, "test", 4u));
+ free(s);
+
+ w = wcsdup((wchar_t[]){11, 22, 33, 0});
+ assert(w);
+ assert(malloc_usable_size(w) >= 4u * sizeof(wchar_t));
+ assert(GET_MEMINFO(w)->requested_alignment == _Alignof(wchar_t));
+ assert((uintptr_t)w % (uintptr_t)_Alignof(wchar_t) == 0u);
+ assert(GET_MEMINFO(w)->requested_alloc_size == 4u * sizeof(wchar_t));
+ EXPECT(!memcmp(w, (wchar_t[]){11, 22, 33, 0}, 4u * sizeof(wchar_t)));
+ free(w);
+
+ w = wcsndup((wchar_t[]){11, 22, 33, 0}, 100u);
+ assert(w);
+ assert(malloc_usable_size(w) >= 4u * sizeof(wchar_t));
+ assert(GET_MEMINFO(w)->requested_alignment == _Alignof(wchar_t));
+ assert((uintptr_t)w % (uintptr_t)_Alignof(wchar_t) == 0u);
+ assert(GET_MEMINFO(w)->requested_alloc_size == 4u * sizeof(wchar_t));
+ EXPECT(!memcmp(w, (wchar_t[]){11, 22, 33, 0}, 4u * sizeof(wchar_t)));
+ free(w);
+
+ w = wcsndup((wchar_t[]){11, 22, 33, 0}, 2u);
+ assert(w);
+ assert(malloc_usable_size(w) >= 3u * sizeof(wchar_t));
+ assert(GET_MEMINFO(w)->requested_alignment == _Alignof(wchar_t));
+ assert((uintptr_t)w % (uintptr_t)_Alignof(wchar_t) == 0u);
+ assert(GET_MEMINFO(w)->requested_alloc_size == 3u * sizeof(wchar_t));
+ EXPECT(!memcmp(w, (wchar_t[]){11, 22, 0}, 3u * sizeof(wchar_t)));
+ free(w);
+}
+
+
+static void
+check_failures(void)
+{
+ void *q;
+
+ libtest_set_alloc_failure_in(1u);
+ errno = 0;
+ EXPECT(!malloc(1u));
+ EXPECT(errno == ENOMEM);
+ EXPECT(!libtest_get_alloc_failure_in());
+
+ libtest_set_alloc_failure_in(1u);
+ errno = 0;
+ EXPECT(!calloc(1u, 1u));
+ EXPECT(errno == ENOMEM);
+ EXPECT(!libtest_get_alloc_failure_in());
+
+ libtest_set_alloc_failure_in(1u);
+ errno = 0;
+ EXPECT(!realloc(NULL, 1u));
+ EXPECT(errno == ENOMEM);
+ EXPECT(!libtest_get_alloc_failure_in());
+ q = realloc(NULL, 1u);
+ assert(q);
+ libtest_set_alloc_failure_in(1u);
+ errno = 0;
+ EXPECT(!realloc(q, 1u));
+ EXPECT(errno == ENOMEM);
+ EXPECT(!libtest_get_alloc_failure_in());
+ free(q);
+
+ libtest_set_alloc_failure_in(1u);
+ errno = 0;
+ EXPECT(!reallocarray(NULL, 1u, 1u));
+ EXPECT(errno == ENOMEM);
+ EXPECT(!libtest_get_alloc_failure_in());
+ q = reallocarray(NULL, 1u, 1u);
+ assert(q);
+ libtest_set_alloc_failure_in(1u);
+ errno = 0;
+ EXPECT(!reallocarray(q, 1u, 1u));
+ EXPECT(errno == ENOMEM);
+ EXPECT(!libtest_get_alloc_failure_in());
+ free(q);
- /* TODO test failures */
+ libtest_set_alloc_failure_in(1u);
+ errno = 0;
+ EXPECT(!memalign(1u, 1u));
+ EXPECT(errno == ENOMEM);
+ EXPECT(!libtest_get_alloc_failure_in());
+
+ libtest_set_alloc_failure_in(1u);
+ errno = 0;
+ EXPECT(!aligned_alloc(1u, 1u));
+ EXPECT(errno == ENOMEM);
+ EXPECT(!libtest_get_alloc_failure_in());
+
+ libtest_set_alloc_failure_in(1u);
+ errno = 0;
+ EXPECT(posix_memalign(&q, sizeof(void *), 1u) == ENOMEM);
+ EXPECT(!libtest_get_alloc_failure_in());
+
+ libtest_set_alloc_failure_in(1u);
+ errno = 0;
+ EXPECT(!valloc(1u));
+ EXPECT(errno == ENOMEM);
+ EXPECT(!libtest_get_alloc_failure_in());
+
+ libtest_set_alloc_failure_in(1u);
+ errno = 0;
+ EXPECT(!pvalloc(1u));
+ EXPECT(errno == ENOMEM);
+ EXPECT(!libtest_get_alloc_failure_in());
+
+ libtest_set_alloc_failure_in(1u);
+ errno = 0;
+ EXPECT(!strdup("x"));
+ EXPECT(errno == ENOMEM);
+ EXPECT(!libtest_get_alloc_failure_in());
+
+ libtest_set_alloc_failure_in(1u);
+ errno = 0;
+ EXPECT(!strndup("x", 1u));
+ EXPECT(errno == ENOMEM);
+ EXPECT(!libtest_get_alloc_failure_in());
+
+ libtest_set_alloc_failure_in(1u);
+ errno = 0;
+ EXPECT(!wcsdup((wchar_t[]){1, 0}));
+ EXPECT(errno == ENOMEM);
+ EXPECT(!libtest_get_alloc_failure_in());
+
+ libtest_set_alloc_failure_in(1u);
+ errno = 0;
+ EXPECT(!wcsndup((wchar_t[]){1, 0}, 1u));
+ EXPECT(errno == ENOMEM);
+ EXPECT(!libtest_get_alloc_failure_in());
+
+ libtest_set_alloc_failure_in(1u);
+ errno = 0;
+ EXPECT(!memdup("x", 1u));
+ EXPECT(errno == ENOMEM);
+ EXPECT(!libtest_get_alloc_failure_in());
+}
+
+
+int
+main(void)
+{
+ SET_UP_ALARM();
+
+ libtest_start_tracking();
+
+ check_successfuls();
+ libtest_set_alloc_failure_in(1000u);
+ check_successfuls();
+ EXPECT(libtest_get_alloc_failure_in() == 1000u - 31u);
+ check_failures();
+
+ p = NULL;
+ free(p);
+
+ libtest_stop_tracking();
+ EXPECT(libtest_check_no_leaks());
+ return 0;
}
diff --git a/libtest/alloc_have_custom.c b/libtest/alloc_have_custom.c
index ae67be0..d532c3f 100644
--- a/libtest/alloc_have_custom.c
+++ b/libtest/alloc_have_custom.c
@@ -21,6 +21,11 @@ PURE size_t (malloc_usable_size)(void *ptr);
void (free)(void *ptr);
void (free_sized)(void *ptr, size_t size);
void (free_aligned_sized)(void *ptr, size_t alignment, size_t size);
+char *(strdup)(const char *s);
+char *(strndup)(const char *s, size_t n);
+wchar_t *(wcsdup)(const wchar_t *s);
+wchar_t *(wcsndup)(const wchar_t *s, size_t n);
+void *(memdup)(const void *s, size_t n);
#if defined(__GNUC__)
# pragma GCC diagnostic pop
@@ -100,6 +105,11 @@ int libtest_have_custom_valloc(void) { CHECK_CUSTOM_ALLOC(freeable_valloc, 1u);
int libtest_have_custom_pvalloc(void) { CHECK_CUSTOM_ALLOC(freeable_pvalloc, 1u); }
int libtest_have_custom_memalign(void) { CHECK_CUSTOM_ALLOC(memalign, 1u, 1u); }
int libtest_have_custom_aligned_alloc(void) { CHECK_CUSTOM_ALLOC(aligned_alloc, 1u, 1u); }
+int libtest_have_custom_strdup(void) { CHECK_CUSTOM_ALLOC(strdup, "x"); }
+int libtest_have_custom_strndup(void) { CHECK_CUSTOM_ALLOC(strndup, "x", 1u); }
+int libtest_have_custom_wcsdup(void) { CHECK_CUSTOM_ALLOC(wcsdup, (wchar_t[]){1, 0}); }
+int libtest_have_custom_wcsndup(void) { CHECK_CUSTOM_ALLOC(wcsndup, &(wchar_t){1}, 1u); }
+int libtest_have_custom_memdup(void) { CHECK_CUSTOM_ALLOC(memdup, "x", 1u); }
int
@@ -228,6 +238,11 @@ main(void)
CHECK(libtest_have_custom_free);
CHECK(libtest_have_custom_free_sized);
CHECK(libtest_have_custom_free_aligned_sized);
+ CHECK(libtest_have_custom_strdup);
+ CHECK(libtest_have_custom_strndup);
+ CHECK(libtest_have_custom_wcsdup);
+ CHECK(libtest_have_custom_wcsndup);
+ CHECK(libtest_have_custom_memdup);
return 0;
}
diff --git a/libtest/common-config.mk b/libtest/common-config.mk
new file mode 100644
index 0000000..65c0ddd
--- /dev/null
+++ b/libtest/common-config.mk
@@ -0,0 +1,16 @@
+C17 !=\
+ if command -v c17 >/dev/null || ! command -v cc >/dev/null; then\
+ echo c17;\
+ else\
+ echo cc -std=c17;\
+ fi
+
+IMPLEMENT_MMAP_CPPFLAGS !=\
+ if $(IMPLEMENT_MMAP); then\
+ echo -DIMPLEMENT_MMAP;\
+ fi
+
+OBJ_MMAP !=\
+ if $(IMPLEMENT_MMAP); then\
+ echo mmap.o;\
+ fi
diff --git a/libtest/common.h b/libtest/common.h
index 070b217..667743a 100644
--- a/libtest/common.h
+++ b/libtest/common.h
@@ -7,13 +7,28 @@
#include <sys/mman.h>
#include <errno.h>
#include <inttypes.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdarg.h>
#include <stdatomic.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <string.h>
#include <unistd.h>
+#include <wchar.h>
+
+
+#if defined(__clang__)
+# pragma clang diagnostic ignored "-Wpre-c11-compat" /* clang is being silly: it complains about _Thread_local */
+# pragma clang diagnostic ignored "-Wc++-keyword" /* clang is being silly: it complains about using wchar_t */
+# pragma clang diagnostic ignored "-Wimplicit-void-ptr-cast" /* clang is being silly: this is C, not C++ */
+# pragma clang diagnostic ignored "-Wunsafe-buffer-usage" /* completely broken warning */
+# pragma clang diagnostic ignored "-Wdisabled-macro-expansion" /* clang is being silly: it is common practice, and it complains about libc code */
+#endif
+
#include "libtest.h"
@@ -76,6 +91,11 @@ enum memory_origin {
FROM_MEMALIGN,
FROM_ALIGNED_ALLOC,
FROM_POSIX_MEMALIGN,
+ FROM_STRDUP,
+ FROM_STRNDUP,
+ FROM_WCSDUP,
+ FROM_WCSNDUP,
+ FROM_MEMDUP,
FROM_MMAP_FILE,
FROM_MMAP_ANON
};
@@ -118,6 +138,11 @@ extern volatile int libtest_malloc_usable_size_is_custom;
extern volatile int libtest_free_is_custom;
extern volatile int libtest_free_sized_is_custom;
extern volatile int libtest_free_aligned_sized_is_custom;
+extern volatile int libtest_strdup_is_custom;
+extern volatile int libtest_strndup_is_custom;
+extern volatile int libtest_wcsdup_is_custom;
+extern volatile int libtest_wcsndup_is_custom;
+extern volatile int libtest_memdup_is_custom;
extern struct meminfo libtest_allocs_head;
extern struct meminfo libtest_allocs_tail;
@@ -130,6 +155,7 @@ extern int libtest_malloc_accept_leakage;
extern _Thread_local size_t libtest_malloc_internal_usage;
extern _Thread_local size_t libtest_kill_malloc_tracking;
+extern _Thread_local size_t libtest_malloc_fail_in;
HIDDEN inline void **
@@ -146,18 +172,41 @@ HIDDEN void *libtest_alloc(struct meminfo *);
HIDDEN void libtest_free(void *, enum libtest_zero_check);
#ifdef WITH_BACKTRACE
-HIDDEN void libtest_print_backtrace(FILE *, const char *indent, size_t first, const struct backtrace *);
+HIDDEN void libtest_print_backtrace(FILE *, const char *prefix, const char *indent,
+ size_t first, const struct backtrace *, ucontext_t *);
#else
-# define libtest_print_backtrace(FP, INDENT, FIRST, BACKTRACE) ((void)0)
+# define libtest_print_backtrace(FP, PREFIX, INDENT, FIRST, BACKTRACE, CONTEXT)\
+ do {\
+ (void) (FP);\
+ (void) (PREFIX);\
+ (void) (INDENT);\
+ (void) (FIRST);\
+ (void) (BACKTRACE);\
+ (void) (CONTEXT);\
+ } while (0)
#endif
+
+#ifdef IMPLEMENT_MMAP
+void *libtest_real_mmap(void *, size_t, int, int, int, off_t);
+int libtest_real_munmap(void *, size_t);
+void *libtest_real_mremap(void *, size_t, size_t, int, ...);
+#else
+# if defined(__clang__)
+# pragma clang diagnostic push
+# pragma clang diagnostic ignored "-Wreserved-identifier"
+# endif
void *__mmap(void *, size_t, int, int, int, off_t);
int __munmap(void *, size_t);
void *__mremap(void *, size_t, size_t, int, ...);
-#define libtest_real_mmap __mmap
-#define libtest_real_munmap __munmap
-#define libtest_real_mremap __mremap
+# define libtest_real_mmap __mmap
+# define libtest_real_munmap __munmap
+# define libtest_real_mremap __mremap
+# if defined(__clang__)
+# pragma clang diagnostic pop
+# endif
+#endif
#define assert(EXPR)\
@@ -165,7 +214,7 @@ void *__mremap(void *, size_t, size_t, int, ...);
if (!(EXPR)) {\
libtest_malloc_internal_usage++;\
fprintf(stderr, "Assetion failure at %s:%i: %s\n", __FILE__, __LINE__, #EXPR);\
- libtest_print_backtrace(stderr, "\t", 0u, NULL);\
+ libtest_print_backtrace(stderr, NULL, "\t", 0u, NULL, NULL);\
exit(2);\
}\
} while (0)
@@ -235,7 +284,7 @@ void *__mremap(void *, size_t, size_t, int, ...);
if (!(EXPR)) {\
libtest_malloc_internal_usage++;\
fprintf(stderr, "Failure at %s:%i: %s\n", __FILE__, __LINE__, #EXPR);\
- libtest_print_backtrace(stderr, "\t", 0u, NULL);\
+ libtest_print_backtrace(stderr, NULL, "\t", 0u, NULL, NULL);\
exit(1);\
}\
} while (0)
diff --git a/libtest/config.mk b/libtest/config.mk
index b2519f0..aa20790 100644
--- a/libtest/config.mk
+++ b/libtest/config.mk
@@ -1,4 +1,5 @@
WITH_BACKTRACE = true
+IMPLEMENT_MMAP = false
TEST_CONFIGFILE = config_backtraces=$(WITH_BACKTRACE).mk
include $(TEST_INCLUDE_PREFIX)$(TEST_CONFIGFILE)
diff --git a/libtest/config_backtraces=false.mk b/libtest/config_backtraces=false.mk
index f508390..633d25a 100644
--- a/libtest/config_backtraces=false.mk
+++ b/libtest/config_backtraces=false.mk
@@ -1,10 +1,6 @@
-C17 !=\
- if command -v c17 >/dev/null || ! command -v cc >/dev/null; then\
- echo c17;\
- else\
- echo cc -std=c17;\
- fi
+include common-config.mk
-TEST_CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE
+TEST_CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE\
+ $(IMPLEMENT_MMAP_CPPFLAGS)
TEST_CFLAGS =
TEST_LDFLAGS =
diff --git a/libtest/config_backtraces=true.mk b/libtest/config_backtraces=true.mk
index 8233aaa..4331d96 100644
--- a/libtest/config_backtraces=true.mk
+++ b/libtest/config_backtraces=true.mk
@@ -1,11 +1,7 @@
-C17 !=\
- if command -v c17 >/dev/null || ! command -v cc >/dev/null; then\
- echo c17;\
- else\
- echo cc -std=c17;\
- fi
+include common-config.mk
-TEST_CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE -DWITH_BACKTRACE
+TEST_CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE\
+ $(IMPLEMENT_MMAP_CPPFLAGS) -DWITH_BACKTRACE
TEST_CFLAGS = -g
TEST_LDFLAGS = -lunwind -ldw
diff --git a/libtest/globals.c b/libtest/globals.c
index 9e9ec2f..430f037 100644
--- a/libtest/globals.c
+++ b/libtest/globals.c
@@ -16,6 +16,11 @@ volatile int libtest_malloc_usable_size_is_custom = -1;
volatile int libtest_free_is_custom = -1;
volatile int libtest_free_sized_is_custom = -1;
volatile int libtest_free_aligned_sized_is_custom = -1;
+volatile int libtest_strdup_is_custom = -1;
+volatile int libtest_strndup_is_custom = -1;
+volatile int libtest_wcsdup_is_custom = -1;
+volatile int libtest_wcsndup_is_custom = -1;
+volatile int libtest_memdup_is_custom = -1;
struct meminfo libtest_allocs_head;
struct meminfo libtest_allocs_tail;
@@ -28,6 +33,7 @@ int libtest_malloc_accept_leakage = 1;
_Thread_local size_t libtest_malloc_internal_usage = 0u;
_Thread_local size_t libtest_kill_malloc_tracking = 0u;
+_Thread_local size_t libtest_malloc_fail_in = 0u;
#else
diff --git a/libtest/libtest.h b/libtest/libtest.h
index 3a3d963..b6484b2 100644
--- a/libtest/libtest.h
+++ b/libtest/libtest.h
@@ -1,23 +1,404 @@
/* See LICENSE file for copyright and license details. */
+#include <stddef.h>
+#include <signal.h>
+
+/**
+ * Start tracking resources which `libtest_check_no_leaks`
+ * will detect if they are not released
+ */
void libtest_start_tracking(void);
+
+/**
+ * Stop tracking resources, so that `libtest_check_no_leaks`
+ * will not detect if they are not released
+ */
void libtest_stop_tracking(void);
+
+/**
+ * Check for resource leaks
+ *
+ * Any leak will be printed to standard error
+ *
+ * @return 1 if there was no leaks, 0 otherwise
+ */
int libtest_check_no_leaks(void);
-void libtest_force_zero_on_alloc(int);
-void libtest_expect_zeroed_on_free(int);
+
+/**
+ * Make all overriden memory allocation functions
+ * full the usable memory area with null bytes,
+ * or disable this feature
+ *
+ * @param enabled 1 to enable, 0 to disable
+ */
+void libtest_force_zero_on_alloc(int enabled);
+
+/**
+ * Make all overriden memory deallocation functions
+ * check that the entire usable memory area is filled
+ * with null bytes, or disable this feature
+ *
+ * @param enabled 1 to enable, 0 to disable
+ */
+void libtest_expect_zeroed_on_free(int enabled);
+
+
+/**
+ * Test whether malloc(3) has been overridden, allowing
+ * allocations to be tracked, and memory allocation
+ * failures to be injected
+ *
+ * Tools like valgrind(1) prevent resource allocation
+ * functions from being overridden as they may override
+ * such functions themselves in order to detect leaks
+ * and invalid memory access patterns
+ *
+ * @return 1 if overridden, 0 otherwise
+ */
int libtest_have_custom_malloc(void);
+
+/**
+ * Test whether calloc(3) has been overridden, allowing
+ * allocations to be tracked, and memory allocation
+ * failures to be injected
+ *
+ * Tools like valgrind(1) prevent resource allocation
+ * functions from being overridden as they may override
+ * such functions themselves in order to detect leaks
+ * and invalid memory access patterns
+ *
+ * @return 1 if overridden, 0 otherwise
+ */
int libtest_have_custom_calloc(void);
+
+/**
+ * Test whether realloc(3) has been overridden, allowing
+ * allocations to be tracked, and memory allocation
+ * failures to be injected
+ *
+ * Tools like valgrind(1) prevent resource allocation
+ * functions from being overridden as they may override
+ * such functions themselves in order to detect leaks
+ * and invalid memory access patterns
+ *
+ * @return 1 if overridden, 0 otherwise
+ */
int libtest_have_custom_realloc(void);
+
+/**
+ * Test whether reallocarray(3) has been overridden,
+ * allowing allocations to be tracked, and memory
+ * allocation failures to be injected
+ *
+ * Tools like valgrind(1) prevent resource allocation
+ * functions from being overridden as they may override
+ * such functions themselves in order to detect leaks
+ * and invalid memory access patterns
+ *
+ * @return 1 if overridden, 0 otherwise
+ */
int libtest_have_custom_reallocarray(void);
+
+/**
+ * Test whether valloc(3) has been overridden, allowing
+ * allocations to be tracked, and memory allocation
+ * failures to be injected
+ *
+ * Tools like valgrind(1) prevent resource allocation
+ * functions from being overridden as they may override
+ * such functions themselves in order to detect leaks
+ * and invalid memory access patterns
+ *
+ * @return 1 if overridden, 0 otherwise
+ */
int libtest_have_custom_valloc(void);
+
+/**
+ * Test whether pvalloc(3) has been overridden, allowing
+ * allocations to be tracked, and memory allocation
+ * failures to be injected
+ *
+ * Tools like valgrind(1) prevent resource allocation
+ * functions from being overridden as they may override
+ * such functions themselves in order to detect leaks
+ * and invalid memory access patterns
+ *
+ * @return 1 if overridden, 0 otherwise
+ */
int libtest_have_custom_pvalloc(void);
+
+/**
+ * Test whether memalign(3) has been overridden, allowing
+ * allocations to be tracked, and memory allocation
+ * failures to be injected
+ *
+ * Tools like valgrind(1) prevent resource allocation
+ * functions from being overridden as they may override
+ * such functions themselves in order to detect leaks
+ * and invalid memory access patterns
+ *
+ * @return 1 if overridden, 0 otherwise
+ */
int libtest_have_custom_memalign(void);
+
+/**
+ * Test whether aligned_alloc(3) has been overridden,
+ * allowing allocations to be tracked, and memory
+ * allocation failures to be injected
+ *
+ * Tools like valgrind(1) prevent resource allocation
+ * functions from being overridden as they may override
+ * such functions themselves in order to detect leaks
+ * and invalid memory access patterns
+ *
+ * @return 1 if overridden, 0 otherwise
+ */
int libtest_have_custom_aligned_alloc(void);
+
+/**
+ * Test whether posix_memalign(3) has been overridden,
+ * allowing allocations to be tracked, and memory
+ * allocation failures to be injected
+ *
+ * Tools like valgrind(1) prevent resource allocation
+ * functions from being overridden as they may override
+ * such functions themselves in order to detect leaks
+ * and invalid memory access patterns
+ *
+ * @return 1 if overridden, 0 otherwise
+ */
int libtest_have_custom_posix_memalign(void);
+
+/**
+ * Test whether strdup(3) has been overridden, allowing
+ * allocations to be tracked, and memory allocation
+ * failures to be injected
+ *
+ * Tools like valgrind(1) prevent resource allocation
+ * functions from being overridden as they may override
+ * such functions themselves in order to detect leaks
+ * and invalid memory access patterns
+ *
+ * @return 1 if overridden, 0 otherwise
+ */
+int libtest_have_custom_strdup(void);
+
+/**
+ * Test whether strndup(3) has been overridden, allowing
+ * allocations to be tracked, and memory allocation
+ * failures to be injected
+ *
+ * Tools like valgrind(1) prevent resource allocation
+ * functions from being overridden as they may override
+ * such functions themselves in order to detect leaks
+ * and invalid memory access patterns
+ *
+ * @return 1 if overridden, 0 otherwise
+ */
+int libtest_have_custom_strndup(void);
+
+/**
+ * Test whether wcsdup(3) has been overridden, allowing
+ * allocations to be tracked, and memory allocation
+ * failures to be injected
+ *
+ * Tools like valgrind(1) prevent resource allocation
+ * functions from being overridden as they may override
+ * such functions themselves in order to detect leaks
+ * and invalid memory access patterns
+ *
+ * @return 1 if overridden, 0 otherwise
+ */
+int libtest_have_custom_wcsdup(void);
+
+/**
+ * Test whether wcsndup(3) has been overridden, allowing
+ * allocations to be tracked, and memory allocation
+ * failures to be injected
+ *
+ * Tools like valgrind(1) prevent resource allocation
+ * functions from being overridden as they may override
+ * such functions themselves in order to detect leaks
+ * and invalid memory access patterns
+ *
+ * @return 1 if overridden, 0 otherwise
+ */
+int libtest_have_custom_wcsndup(void);
+
+/**
+ * Test whether memdup(3) has been overridden, allowing
+ * allocations to be tracked, and memory allocation
+ * failures to be injected
+ *
+ * Tools like valgrind(1) prevent resource allocation
+ * functions from being overridden as they may override
+ * such functions themselves in order to detect leaks
+ * and invalid memory access patterns
+ *
+ * @return 1 if overridden, 0 otherwise
+ */
+int libtest_have_custom_memdup(void);
+
+/**
+ * Test whether malloc_usable_size(3) has been overridden,
+ * allowing it to be used for override implementations of
+ * memory allocation functions
+ *
+ * Tools like valgrind(1) prevent resource allocation
+ * inspection functions from being overridden as they
+ * may override such functions themselves as they may
+ * override memory allocation functions to detect leaks
+ * and invalid memory access patterns
+ *
+ * @return 1 if overridden, 0 otherwise
+ */
int libtest_have_custom_malloc_usable_size(void);
+
+/**
+ * Test whether free(3) has been overridden, allowing it
+ * to be used for override implementations of memory
+ * allocation functions
+ *
+ * Tools like valgrind(1) prevent resource deallocation
+ * functions from being overridden as they may override
+ * such functions themselves as they may override memory
+ * allocation functions to detect leaks and invalid
+ * memory access patterns
+ *
+ * You don't have to call this function yourself,
+ * overriden memory allocation function will call it
+ * and abort the process if this function return 0,
+ * likewise free(3) will abort the process if one of
+ * the memory allocation functions have not been
+ * overriden
+ *
+ * @return 1 if overridden, 0 otherwise
+ */
int libtest_have_custom_free(void);
+
+/**
+ * Test whether free_sized(3) has been overridden,
+ * allowing it to be used for override implementations
+ * of memory allocation functions
+ *
+ * Tools like valgrind(1) prevent resource deallocation
+ * functions from being overridden as they may override
+ * such functions themselves as they may override memory
+ * allocation functions to detect leaks and invalid
+ * memory access patterns
+ *
+ * You don't have to call this function yourself,
+ * overriden memory allocation function will call it
+ * and abort the process if this function return 0,
+ * likewise free_sized(3) will abort the process if
+ * one of the memory allocation functions, whose
+ * alloactions it can deallocate, have not been
+ * overriden
+ *
+ * @return 1 if overridden, 0 otherwise
+ */
int libtest_have_custom_free_sized(void);
+
+/**
+ * Test whether free_aligned_sized(3) has been overridden,
+ * allowing it to be used for override implementations
+ * of memory allocation functions
+ *
+ * Tools like valgrind(1) prevent resource deallocation
+ * functions from being overridden as they may override
+ * such functions themselves as they may override memory
+ * allocation functions to detect leaks and invalid
+ * memory access patterns
+ *
+ * You don't have to call this function yourself,
+ * overriden memory allocation function will call it
+ * and abort the process if this function return 0,
+ * likewise free_aligned_sized(3) will abort the process
+ * if one of the memory allocation functions, whose
+ * alloactions it can deallocate, have not been
+ * overriden
+ *
+ * @return 1 if overridden, 0 otherwise
+ */
int libtest_have_custom_free_aligned_sized(void);
-void libtest_dump_stack(const char *indent);
+
+/**
+ * Print a stack trace, to standard error, provided
+ * that backtrace support was enabled at compile-time
+ *
+ * @param prefix The text to print at the beginning of the first line
+ * @param indent The text to print at the beginning of the other lines
+ *
+ * If one if `prefix` or `indent` is `NULL`, the other
+ * will be used as both. Both may not be `NULL` at the
+ * same time.
+ */
+void libtest_dump_stack(const char *prefix, const char *indent);
+
+/**
+ * Set up a signal handler to print a stack trace when
+ * a signal is received
+ *
+ * This function has no affect if backtrace support
+ * was disbled at compile-time, and therefore `*old_out`
+ * is only set if backtrace support was enabled
+ *
+ * @param signo The signal to catch
+ * @param old_out Output parameter for the older signal handler
+ */
+void libtest_stack_on_signal(int signo, struct sigaction *old_out);
+
+/**
+ * Undo `libtest_stack_on_signal`
+ *
+ * This function has no affect if backtrace support
+ * was disbled at compile-time, making it safe,
+ * unlike sigaction(3), to use to to restore the
+ * old signal handler returned by `libtest_stack_on_signal`
+ * when backtrace support is disabled. Additionally,
+ * this function will deallocate the alternative stack
+ * set up by `libtest_stack_on_signal` once it has been
+ * called as many times as `libtest_stack_on_signal`.
+ *
+ * @param signo The signal handler to stop catching
+ * @param old The old signal handler to restore
+ */
+void libtest_stop_stack_on_signal(int signo, const struct sigaction *old);
+
+
+/**
+ * Get the number of allocation attempts
+ * before the next allocation will fail
+ * (ENOMEM), including that attempt in
+ * the count
+ *
+ * @return 0 if no memory allocation failure is scheduled,
+ * 1 if the next memory allocation will fail,
+ * 2 if the attempt after that will fail,
+ * and so on
+ */
+#if defined(__GNUC__)
+__attribute__((__pure__))
+#endif
+size_t libtest_get_alloc_failure_in(void);
+
+/**
+ * Get the number of allocation attempts
+ * before the next allocation will fail
+ * (ENOMEM), including that attempt in
+ * the count
+ *
+ * @param n 0 if no memory allocation should fail,
+ * 1 if the next memory allocation shall fail,
+ * 2 if the attempt after that shall fail,
+ * and so on
+ *
+ * Once the scheduled memory allocation
+ * failure as occurred, the schedule is
+ * cleared and the next attempt will be
+ * successful (provided that there isn't
+ * a real failure)
+ */
+void libtest_set_alloc_failure_in(size_t n);
diff --git a/libtest/libtest_alloc.c b/libtest/libtest_alloc.c
index 5aa3218..dc9cc8f 100644
--- a/libtest/libtest_alloc.c
+++ b/libtest/libtest_alloc.c
@@ -62,7 +62,7 @@ libtest_alloc(struct meminfo *meminfo)
size_t bookkeeping;
int saved_errno;
#ifdef WITH_BACKTRACE
- size_t backtrace_realignment;
+ size_t backtrace_realignment = 0u; /* initialised to make clang(1) happy */
size_t i, backtrace_n;
unw_cursor_t cursor;
unw_context_t context;
@@ -121,6 +121,12 @@ libtest_alloc(struct meminfo *meminfo)
bookkeeping -= meminfo->actual_alignment;
/* Allocate memory */
+ if (!libtest_malloc_internal_usage) {
+ if (libtest_malloc_fail_in && !--libtest_malloc_fail_in) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ }
base_ptr = mmap_anon(meminfo->real_alloc_size);
if (!base_ptr)
return NULL;
@@ -152,6 +158,13 @@ libtest_alloc(struct meminfo *meminfo)
meminfo->usable_alloc_size = meminfo->real_alloc_size;
meminfo->usable_alloc_size -= (size_t)((char *)ret_ptr - (char *)base_ptr);
+ /* See CEVEATS section in mmap(2) */
+ if (meminfo->usable_alloc_size > PTRDIFF_MAX) {
+ assert(!libtest_real_munmap(base_ptr, meminfo->real_alloc_size));
+ errno = ENOMEM;
+ return NULL;
+ }
+
/* Store book-keeping */
meminfo->usable_area = ret_ptr;
memcpy(base_ptr, meminfo, sizeof(*meminfo));
@@ -183,7 +196,7 @@ libtest_alloc(struct meminfo *meminfo)
libtest_malloc_internal_usage++;
fprintf(stderr, "Memory allocated: %p (alloc-size=%zu, real-size=%zu, leak-allowed=%i)\n",
ret_ptr, meminfo->requested_alloc_size, meminfo->real_alloc_size, meminfo->accept_leakage);
- libtest_print_backtrace(stderr, "\tat ", 0u, NULL);
+ libtest_print_backtrace(stderr, NULL, "\tat ", 0u, NULL, NULL);
fflush(stderr);
libtest_malloc_internal_usage--;
}
diff --git a/libtest/libtest_check_no_leaks.c b/libtest/libtest_check_no_leaks.c
index 15f7e58..40e3a80 100644
--- a/libtest/libtest_check_no_leaks.c
+++ b/libtest/libtest_check_no_leaks.c
@@ -30,7 +30,7 @@ check_no_memory_leaks(void)
mem->usable_area, mem->requested_alloc_size);
#ifdef WITH_BACKTRACE
if (mem->backtrace)
- libtest_print_backtrace(stderr, "\tat ", 0u, mem->backtrace);
+ libtest_print_backtrace(stderr, NULL, "\tat ", 0u, mem->backtrace, NULL);
#endif
fflush(stderr);
}
diff --git a/libtest/libtest_dump_stack.c b/libtest/libtest_dump_stack.c
index ca0c9c7..030da8c 100644
--- a/libtest/libtest_dump_stack.c
+++ b/libtest/libtest_dump_stack.c
@@ -10,12 +10,13 @@
CONST
#endif
void
-libtest_dump_stack(const char *indent)
+libtest_dump_stack(const char *prefix, const char *indent)
{
#ifndef WITH_BACKTRACE
+ (void) prefix;
(void) indent;
#else
- libtest_print_backtrace(stderr, indent, 1u, NULL);
+ libtest_print_backtrace(stderr, prefix, indent, 1u, NULL, NULL);
#endif
}
diff --git a/libtest/libtest_free.c b/libtest/libtest_free.c
index 398994b..3dcd41c 100644
--- a/libtest/libtest_free.c
+++ b/libtest/libtest_free.c
@@ -24,7 +24,7 @@ libtest_free(void *ptr, enum libtest_zero_check zero_checking)
if (!inside_free && getenv("PRETRACE_FREE")) {
inside_free = 1;
fprintf(stderr, "Deallocating: %p\n", ptr);
- libtest_print_backtrace(stderr, "\tat ", 0u, NULL);
+ libtest_print_backtrace(stderr, NULL, "\tat ", 0u, NULL, NULL);
fflush(stderr);
inside_free = 0;
}
@@ -66,7 +66,7 @@ libtest_free(void *ptr, enum libtest_zero_check zero_checking)
fprintf(stderr, "Memory deallocated: %p\n (alloc-size=%zu, real-size=%zu)",
ptr, mem->requested_alloc_size, mem->real_alloc_size);
if (getenv("TRACE_FREE") && !getenv("PRETRACE_FREE"))
- libtest_print_backtrace(stderr, "\tat ", 0u, NULL);
+ libtest_print_backtrace(stderr, NULL, "\tat ", 0u, NULL, NULL);
fflush(stderr);
inside_free = 0;
}
diff --git a/libtest/libtest_get_alloc_failure_in.c b/libtest/libtest_get_alloc_failure_in.c
new file mode 100644
index 0000000..347c973
--- /dev/null
+++ b/libtest/libtest_get_alloc_failure_in.c
@@ -0,0 +1,31 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+size_t
+libtest_get_alloc_failure_in(void)
+{
+ return libtest_malloc_fail_in;
+}
+
+
+#else
+
+
+int
+main(void)
+{
+ EXPECT(libtest_malloc_fail_in == 0u);
+ EXPECT(libtest_get_alloc_failure_in() == 0u);
+ EXPECT(libtest_get_alloc_failure_in() == 0u);
+ EXPECT(libtest_malloc_fail_in == 0u);
+ libtest_malloc_fail_in = 4u;
+ EXPECT(libtest_get_alloc_failure_in() == 4u);
+ EXPECT(libtest_get_alloc_failure_in() == 4u);
+ EXPECT(libtest_malloc_fail_in == 4u);
+ return 0;
+}
+
+
+#endif
diff --git a/libtest/libtest_print_backtrace.c b/libtest/libtest_print_backtrace.c
index 8fa0fd5..e595131 100644
--- a/libtest/libtest_print_backtrace.c
+++ b/libtest/libtest_print_backtrace.c
@@ -9,7 +9,8 @@
void
-libtest_print_backtrace(FILE *fp, const char *indent, size_t first, const struct backtrace *backtrace)
+libtest_print_backtrace(FILE *fp, const char *prefix, const char *indent, size_t first,
+ const struct backtrace *backtrace, ucontext_t *ucontext)
{
static _Thread_local int recursion_guard = 0;
int saved_errno;
@@ -35,7 +36,21 @@ libtest_print_backtrace(FILE *fp, const char *indent, size_t first, const struct
recursion_guard = 1;
libtest_malloc_internal_usage++;
- if (!backtrace) {
+ if (!prefix)
+ prefix = indent;
+ if (!indent)
+ indent = prefix;
+ if (!prefix || !indent)
+ abort();
+
+ if (backtrace) {
+ if (ucontext) {
+ abort();
+ }
+ } else if (ucontext) {
+ if (unw_init_local2(&cursor, (unw_context_t *)ucontext, UNW_INIT_SIGNAL_FRAME))
+ goto out;
+ } else {
if (unw_getcontext(&context))
goto out;
if (unw_init_local(&cursor, &context))
@@ -86,14 +101,15 @@ libtest_print_backtrace(FILE *fp, const char *indent, size_t first, const struct
#endif
#if defined(HAVE_LINE_INFO)
- fprintf(fp, "%s0x%016"PRIxPTR": %s", indent, (uintptr_t)ip, funcname ? funcname : "???");
+ fprintf(fp, "%s0x%016"PRIxPTR": %s", prefix, (uintptr_t)ip, funcname ? funcname : "???");
if (line)
fprintf(fp, " (%s:%i)\n", filename, lineno);
else
fprintf(fp, "\n");
#else
- fprintf(fp, "%s0x%016"PRIxPTR"\n", indent, (uintptr_t)ip);
+ fprintf(fp, "%s0x%016"PRIxPTR"\n", prefix, (uintptr_t)ip);
#endif
+ prefix = indent;
}
#if defined(HAVE_LINE_INFO)
diff --git a/libtest/libtest_set_alloc_failure_in.c b/libtest/libtest_set_alloc_failure_in.c
new file mode 100644
index 0000000..9811456
--- /dev/null
+++ b/libtest/libtest_set_alloc_failure_in.c
@@ -0,0 +1,32 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+
+void
+libtest_set_alloc_failure_in(size_t n)
+{
+ libtest_malloc_fail_in = n;
+}
+
+
+#else
+
+
+int
+main(void)
+{
+ EXPECT(libtest_malloc_fail_in == 0u);
+ libtest_set_alloc_failure_in(4u);
+ EXPECT(libtest_malloc_fail_in == 4u);
+ libtest_set_alloc_failure_in(4u);
+ EXPECT(libtest_malloc_fail_in == 4u);
+ libtest_set_alloc_failure_in(0u);
+ EXPECT(libtest_malloc_fail_in == 0u);
+ libtest_set_alloc_failure_in(0u);
+ EXPECT(libtest_malloc_fail_in == 0u);
+ return 0;
+}
+
+
+#endif
diff --git a/libtest/libtest_stack_on_signal.c b/libtest/libtest_stack_on_signal.c
new file mode 100644
index 0000000..0f92972
--- /dev/null
+++ b/libtest/libtest_stack_on_signal.c
@@ -0,0 +1,102 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+# ifdef WITH_BACKTRACE
+
+
+static void
+stack_dumper(int sig, siginfo_t *info, void *ucontext)
+{
+ (void) sig;
+ (void) info;
+ libtest_print_backtrace(stderr, "crashed at ", " by ", 0u, NULL, ucontext);
+}
+
+
+static stack_t ss;
+static size_t have_ss = 0u;
+
+
+static void
+create_altstack(void)
+{
+ if (have_ss++)
+ return;
+ ss.ss_sp = libtest_real_mmap(NULL, (size_t)SIGSTKSZ, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ assert(ss.ss_sp != MAP_FAILED);
+ ss.ss_size = (size_t)SIGSTKSZ;
+ ss.ss_flags = 0;
+ assert(!sigaltstack(&ss, NULL));
+}
+
+
+static void
+destroy_altstack(void)
+{
+ if (--have_ss)
+ return;
+ assert(!libtest_real_munmap(ss.ss_sp, ss.ss_size));
+}
+
+
+void
+libtest_stack_on_signal(int signo, struct sigaction *old_out)
+{
+ struct sigaction sa;
+
+ create_altstack();
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = &stack_dumper;
+ sa.sa_flags = (int)(SA_RESETHAND | SA_SIGINFO);
+ if (have_ss)
+ sa.sa_flags |= SA_ONSTACK;
+ sigfillset(&sa.sa_mask);
+
+ assert(!sigaction(signo, &sa, old_out));
+}
+
+
+void
+libtest_stop_stack_on_signal(int signo, const struct sigaction *old)
+{
+ assert(!sigaction(signo, old, NULL));
+ destroy_altstack();
+}
+
+
+#else
+
+
+void
+libtest_stack_on_signal(int signo, struct sigaction *old_out)
+{
+ (void) signo;
+ (void) old_out;
+}
+
+void
+libtest_stop_stack_on_signal(int signo, const struct sigaction *old)
+{
+ (void) signo;
+ (void) old;
+}
+
+
+# endif
+
+
+#else
+
+
+CONST int
+main(void)
+{
+ /* How would one even test this, and what would be the point? */
+ return 0;
+}
+
+
+#endif
diff --git a/libtest/mmap.c b/libtest/mmap.c
new file mode 100644
index 0000000..c12594f
--- /dev/null
+++ b/libtest/mmap.c
@@ -0,0 +1,72 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+#ifndef TEST
+
+#include <sys/syscall.h>
+
+
+void *
+libtest_real_mmap(void *addr, size_t len, int prot, int flags, int fd, off_t off)
+{
+ size_t pagesize = libtest_get_pagesize();
+
+ if (off < 0 || off % (off_t)pagesize)
+ goto einval;
+ off /= (off_t)pagesize;
+
+#if defined(__x86_64__) && defined(__ILP32__) /* x32 */
+ if (off > LLONG_MAX)
+ goto einval;
+#else
+ if (off > LONG_MAX)
+ goto einval;
+#endif
+
+#ifdef SYS_mmap2
+ assert(pagesize == 4096u);
+ return (void *)syscall(SYS_mmap2, addr, len, prot, flags, fd, off);
+#else
+ return (void *)syscall(SYS_mmap, addr, len, prot, flags, fd, off);
+#endif
+
+einval:
+ errno = EINVAL;
+ return MAP_FAILED;
+}
+
+
+int
+libtest_real_munmap(void *addr, size_t len)
+{
+ return syscall(SYS_munmap, addr, len);
+}
+
+
+void *
+libtest_real_mremap(void *old_addr, size_t old_len, size_t new_len, int flags, ...)
+{
+ va_list args;
+ void *new_addr = NULL;
+
+ if (flags & MREMAP_FIXED) {
+ va_start(args, flags);
+ new_addr = va_arg(args, void *);
+ va_end(args);
+ }
+
+ return (void *)syscall(SYS_mremap, old_addr, old_len, new_len, flags, new_addr);
+}
+
+
+#else
+
+
+CONST int
+main(void)
+{
+ /* TODO maybe test */
+ return 0;
+}
+
+
+#endif