mirror of
https://github.com/jemalloc/jemalloc.git
synced 2026-07-25 14:23:12 +00:00
Add TSD reentrant stress test
This commit is contained in:
committed by
Slobodan Predolac
parent
7ce8b9165d
commit
7c34a482c1
@@ -367,7 +367,8 @@ TESTS_ANALYZE := $(srcroot)test/analyze/prof_bias.c \
|
|||||||
TESTS_STRESS := $(srcroot)test/stress/fill_flush.c \
|
TESTS_STRESS := $(srcroot)test/stress/fill_flush.c \
|
||||||
$(srcroot)test/stress/large_microbench.c \
|
$(srcroot)test/stress/large_microbench.c \
|
||||||
$(srcroot)test/stress/mallctl.c \
|
$(srcroot)test/stress/mallctl.c \
|
||||||
$(srcroot)test/stress/microbench.c
|
$(srcroot)test/stress/microbench.c \
|
||||||
|
$(srcroot)test/stress/tsd_reentrant.c
|
||||||
ifeq (@enable_cxx@, 1)
|
ifeq (@enable_cxx@, 1)
|
||||||
TESTS_STRESS_CPP := $(srcroot)test/stress/cpp/microbench.cpp
|
TESTS_STRESS_CPP := $(srcroot)test/stress/cpp/microbench.cpp
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
extern JEMALLOC_EXPORT void (*test_hooks_arena_new_hook)(void);
|
extern JEMALLOC_EXPORT void (*test_hooks_arena_new_hook)(void);
|
||||||
extern JEMALLOC_EXPORT void (*test_hooks_libc_hook)(void);
|
extern JEMALLOC_EXPORT void (*test_hooks_libc_hook)(void);
|
||||||
extern JEMALLOC_EXPORT void (*test_hooks_safety_check_abort)(const char *);
|
extern JEMALLOC_EXPORT void (*test_hooks_safety_check_abort)(const char *);
|
||||||
|
extern JEMALLOC_EXPORT void (*test_hooks_tsd_bootstrap_hook)(void);
|
||||||
|
|
||||||
#if defined(JEMALLOC_JET) || defined(JEMALLOC_UNIT_TEST)
|
#if defined(JEMALLOC_JET) || defined(JEMALLOC_UNIT_TEST)
|
||||||
# define JEMALLOC_TEST_HOOK(fn, hook) \
|
# define JEMALLOC_TEST_HOOK(fn, hook) \
|
||||||
|
|||||||
@@ -1051,6 +1051,9 @@ tsd_tcache_enabled_data_init(tsd_t *tsd) {
|
|||||||
*/
|
*/
|
||||||
tcache_default_settings_init(tsd_tcache_slowp_get(tsd));
|
tcache_default_settings_init(tsd_tcache_slowp_get(tsd));
|
||||||
tsd_slow_update(tsd);
|
tsd_slow_update(tsd);
|
||||||
|
if (test_hooks_tsd_bootstrap_hook != NULL) {
|
||||||
|
test_hooks_tsd_bootstrap_hook();
|
||||||
|
}
|
||||||
|
|
||||||
if (opt_tcache) {
|
if (opt_tcache) {
|
||||||
/* Trigger tcache init. */
|
/* Trigger tcache init. */
|
||||||
|
|||||||
@@ -13,3 +13,6 @@ void (*test_hooks_libc_hook)(void) = NULL;
|
|||||||
|
|
||||||
JEMALLOC_EXPORT
|
JEMALLOC_EXPORT
|
||||||
void (*test_hooks_safety_check_abort)(const char *) = NULL;
|
void (*test_hooks_safety_check_abort)(const char *) = NULL;
|
||||||
|
|
||||||
|
JEMALLOC_EXPORT
|
||||||
|
void (*test_hooks_tsd_bootstrap_hook)(void) = NULL;
|
||||||
|
|||||||
140
test/stress/tsd_reentrant.c
Normal file
140
test/stress/tsd_reentrant.c
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
#include "test/jemalloc_test.h"
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
|
||||||
|
extern void (*je_test_hooks_tsd_bootstrap_hook)(void);
|
||||||
|
|
||||||
|
static atomic_zu_t handler_allocs;
|
||||||
|
|
||||||
|
static atomic_b_t crash_reported;
|
||||||
|
static bool hook_ran;
|
||||||
|
static volatile sig_atomic_t in_reentrant_signal_alloc;
|
||||||
|
|
||||||
|
static void *volatile signal_alloc;
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_crash(int signo) {
|
||||||
|
#define WRITE_LITERAL(s) (void)write(STDERR_FILENO, s, sizeof(s) - 1)
|
||||||
|
if (atomic_exchange_b(&crash_reported, true, ATOMIC_RELAXED)) {
|
||||||
|
_exit(128 + signo);
|
||||||
|
}
|
||||||
|
|
||||||
|
WRITE_LITERAL("\n*** tsd_reentrant reproduced allocator crash ***\n");
|
||||||
|
if (signo == SIGSEGV) {
|
||||||
|
WRITE_LITERAL("signal: SIGSEGV\n");
|
||||||
|
} else if (signo == SIGBUS) {
|
||||||
|
WRITE_LITERAL("signal: SIGBUS\n");
|
||||||
|
}
|
||||||
|
WRITE_LITERAL("inside SIGUSR1 handler malloc: ");
|
||||||
|
if (in_reentrant_signal_alloc) {
|
||||||
|
WRITE_LITERAL("yes\n");
|
||||||
|
} else {
|
||||||
|
WRITE_LITERAL("no\n");
|
||||||
|
}
|
||||||
|
WRITE_LITERAL("This matches recursive allocation during TSD/tcache "
|
||||||
|
"bootstrap.\n");
|
||||||
|
#undef WRITE_LITERAL
|
||||||
|
|
||||||
|
_exit(128 + signo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_signal(int signo) {
|
||||||
|
(void)signo;
|
||||||
|
in_reentrant_signal_alloc = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This intentionally models asynchronous interruption that allocates.
|
||||||
|
* jemalloc does not promise that arbitrary signal-handler allocation is
|
||||||
|
* safe, but the allocator should keep its own bootstrap state
|
||||||
|
* internally consistent if such reentrancy occurs.
|
||||||
|
*/
|
||||||
|
void *p = malloc(16);
|
||||||
|
if (p != NULL) {
|
||||||
|
signal_alloc = p;
|
||||||
|
atomic_load_add_store_zu(&handler_allocs, 1);
|
||||||
|
}
|
||||||
|
in_reentrant_signal_alloc = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
tsd_bootstrap_signal_hook(void) {
|
||||||
|
hook_ran = true;
|
||||||
|
je_test_hooks_tsd_bootstrap_hook = NULL;
|
||||||
|
/*
|
||||||
|
* The hook only selects the bootstrap window. The recursive allocation
|
||||||
|
* still enters through the process signal path and public malloc().
|
||||||
|
*/
|
||||||
|
raise(SIGUSR1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
worker_start(void *arg) {
|
||||||
|
(void)arg;
|
||||||
|
void *p = malloc(16);
|
||||||
|
expect_ptr_not_null(p, "Unexpected malloc() failure");
|
||||||
|
if (p != NULL) {
|
||||||
|
memset(p, 0xa5, 16);
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
je_test_hooks_tsd_bootstrap_hook = NULL;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
init_signal_handler(void) {
|
||||||
|
struct sigaction sa;
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sa.sa_handler = on_signal;
|
||||||
|
sa.sa_flags = SA_RESTART;
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
expect_d_eq(sigaction(SIGUSR1, &sa, NULL), 0,
|
||||||
|
"Unexpected sigaction() failure");
|
||||||
|
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sa.sa_handler = on_crash;
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
expect_d_eq(sigaction(SIGSEGV, &sa, NULL), 0,
|
||||||
|
"Unexpected sigaction() failure");
|
||||||
|
expect_d_eq(sigaction(SIGBUS, &sa, NULL), 0,
|
||||||
|
"Unexpected sigaction() failure");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_BEGIN(test_tsd_bootstrap_signal_reentrant) {
|
||||||
|
thd_t worker;
|
||||||
|
|
||||||
|
init_signal_handler();
|
||||||
|
|
||||||
|
atomic_store_b(&crash_reported, false, ATOMIC_RELEASE);
|
||||||
|
atomic_store_zu(&handler_allocs, 0, ATOMIC_RELEASE);
|
||||||
|
hook_ran = false;
|
||||||
|
|
||||||
|
je_test_hooks_tsd_bootstrap_hook = tsd_bootstrap_signal_hook;
|
||||||
|
thd_create(&worker, worker_start, NULL);
|
||||||
|
thd_join(worker, NULL);
|
||||||
|
je_test_hooks_tsd_bootstrap_hook = NULL;
|
||||||
|
|
||||||
|
expect_true(hook_ran, "TSD bootstrap hook should have executed");
|
||||||
|
expect_zu_eq(atomic_load_zu(&handler_allocs, ATOMIC_ACQUIRE), 1,
|
||||||
|
"Signal handler should have allocated");
|
||||||
|
}
|
||||||
|
TEST_END
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int
|
||||||
|
main(void) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
test_skip("Signals are unavailable");
|
||||||
|
return test_status_skip;
|
||||||
|
#else
|
||||||
|
return test_no_reentrancy(test_tsd_bootstrap_signal_reentrant);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user