diff --git a/include/jemalloc/internal/inspect.h b/include/jemalloc/internal/inspect.h index d8723f96..e8ed44d3 100644 --- a/include/jemalloc/internal/inspect.h +++ b/include/jemalloc/internal/inspect.h @@ -11,7 +11,8 @@ */ /* - * The following struct is for experimental purposes. See + * The following two structs are for experimental purposes. See + * experimental_utilization_query_ctl and * experimental_utilization_batch_query_ctl in src/ctl.c. */ typedef struct inspect_extent_util_stats_s inspect_extent_util_stats_t; @@ -21,7 +22,22 @@ struct inspect_extent_util_stats_s { size_t size; }; +typedef struct inspect_extent_util_stats_verbose_s + inspect_extent_util_stats_verbose_t; + +struct inspect_extent_util_stats_verbose_s { + void *slabcur_addr; + size_t nfree; + size_t nregs; + size_t size; + size_t bin_nfree; + size_t bin_nregs; +}; + void inspect_extent_util_stats_get( tsdn_t *tsdn, const void *ptr, size_t *nfree, size_t *nregs, size_t *size); +void inspect_extent_util_stats_verbose_get(tsdn_t *tsdn, const void *ptr, + size_t *nfree, size_t *nregs, size_t *size, size_t *bin_nfree, + size_t *bin_nregs, void **slabcur_addr); #endif /* JEMALLOC_INTERNAL_INSPECT_H */ diff --git a/include/jemalloc/internal/jemalloc_internal_externs.h b/include/jemalloc/internal/jemalloc_internal_externs.h index cd458ff8..b5b12e91 100644 --- a/include/jemalloc/internal/jemalloc_internal_externs.h +++ b/include/jemalloc/internal/jemalloc_internal_externs.h @@ -46,7 +46,6 @@ extern bool opt_disable_large_size_classes; extern const char *opt_malloc_conf_symlink; extern const char *opt_malloc_conf_env_var; -extern const char *je_malloc_conf_2_conf_harder; /* Escape free-fastpath when ptr & mask == 0 (for sanitization purpose). */ extern uintptr_t san_cache_bin_nonfast_mask; diff --git a/include/jemalloc/internal/safety_check.h b/include/jemalloc/internal/safety_check.h index 360e4aa9..2b4b2d0e 100644 --- a/include/jemalloc/internal/safety_check.h +++ b/include/jemalloc/internal/safety_check.h @@ -11,6 +11,11 @@ void safety_check_fail_sized_dealloc( bool current_dealloc, const void *ptr, size_t true_size, size_t input_size); void safety_check_fail(const char *format, ...); +typedef void (*safety_check_abort_hook_t)(const char *message); + +/* Can set to NULL for a default. */ +void safety_check_set_abort(safety_check_abort_hook_t abort_fn); + #define REDZONE_SIZE ((size_t)32) #define REDZONE_FILL_VALUE 0xBC diff --git a/include/jemalloc/internal/test_hooks.h b/include/jemalloc/internal/test_hooks.h index 9df15383..35f3a211 100644 --- a/include/jemalloc/internal/test_hooks.h +++ b/include/jemalloc/internal/test_hooks.h @@ -5,7 +5,6 @@ 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_safety_check_abort)(const char *); #if defined(JEMALLOC_JET) || defined(JEMALLOC_UNIT_TEST) # define JEMALLOC_TEST_HOOK(fn, hook) \ diff --git a/src/ctl.c b/src/ctl.c index d39b5a08..e048135a 100644 --- a/src/ctl.c +++ b/src/ctl.c @@ -173,6 +173,7 @@ CTL_PROTO(opt_process_madvise_max_batch) CTL_PROTO(opt_malloc_conf_symlink) CTL_PROTO(opt_malloc_conf_env_var) CTL_PROTO(opt_malloc_conf_global_var) +CTL_PROTO(opt_malloc_conf_global_var_2_conf_harder) CTL_PROTO(tcache_create) CTL_PROTO(tcache_flush) CTL_PROTO(tcache_destroy) @@ -372,7 +373,11 @@ CTL_PROTO(experimental_hooks_prof_dump) CTL_PROTO(experimental_hooks_prof_sample) CTL_PROTO(experimental_hooks_prof_sample_free) CTL_PROTO(experimental_hooks_thread_event) +CTL_PROTO(experimental_hooks_safety_check_abort) +CTL_PROTO(experimental_utilization_query) CTL_PROTO(experimental_utilization_batch_query) +CTL_PROTO(experimental_arenas_i_pactivep) +INDEX_PROTO(experimental_arenas_i) CTL_PROTO(experimental_prof_recent_alloc_max) CTL_PROTO(experimental_prof_recent_alloc_dump) CTL_PROTO(experimental_arenas_create_ext) @@ -463,7 +468,9 @@ static const ctl_named_node_t config_node[] = { static const ctl_named_node_t opt_malloc_conf_node[] = { {NAME("symlink"), CTL(opt_malloc_conf_symlink)}, {NAME("env_var"), CTL(opt_malloc_conf_env_var)}, - {NAME("global_var"), CTL(opt_malloc_conf_global_var)}}; + {NAME("global_var"), CTL(opt_malloc_conf_global_var)}, + {NAME("global_var_2_conf_harder"), + CTL(opt_malloc_conf_global_var_2_conf_harder)}}; static const ctl_named_node_t opt_node[] = {{NAME("abort"), CTL(opt_abort)}, {NAME("abort_conf"), CTL(opt_abort_conf)}, @@ -906,12 +913,22 @@ static const ctl_named_node_t experimental_hooks_node[] = { {NAME("prof_dump"), CTL(experimental_hooks_prof_dump)}, {NAME("prof_sample"), CTL(experimental_hooks_prof_sample)}, {NAME("prof_sample_free"), CTL(experimental_hooks_prof_sample_free)}, + {NAME("safety_check_abort"), CTL(experimental_hooks_safety_check_abort)}, {NAME("thread_event"), CTL(experimental_hooks_thread_event)}, }; static const ctl_named_node_t experimental_utilization_node[] = { + {NAME("query"), CTL(experimental_utilization_query)}, {NAME("batch_query"), CTL(experimental_utilization_batch_query)}}; +static const ctl_named_node_t experimental_arenas_i_node[] = { + {NAME("pactivep"), CTL(experimental_arenas_i_pactivep)}}; +static const ctl_named_node_t super_experimental_arenas_i_node[] = { + {NAME(""), CHILD(named, experimental_arenas_i)}}; + +static const ctl_indexed_node_t experimental_arenas_node[] = { + {INDEX(experimental_arenas_i)}}; + static const ctl_named_node_t experimental_prof_recent_node[] = { {NAME("alloc_max"), CTL(experimental_prof_recent_alloc_max)}, {NAME("alloc_dump"), CTL(experimental_prof_recent_alloc_dump)}, @@ -920,6 +937,7 @@ static const ctl_named_node_t experimental_prof_recent_node[] = { static const ctl_named_node_t experimental_node[] = { {NAME("hooks"), CHILD(named, experimental_hooks)}, {NAME("utilization"), CHILD(named, experimental_utilization)}, + {NAME("arenas"), CHILD(indexed, experimental_arenas)}, {NAME("arenas_create_ext"), CTL(experimental_arenas_create_ext)}, {NAME("prof_recent"), CHILD(named, experimental_prof_recent)}}; @@ -963,41 +981,8 @@ ctl_accum_atomic_zu(atomic_zu_t *dst, atomic_zu_t *src) { /******************************************************************************/ -/* - * Historical compatibility for treating arena. as the merged - * all-arenas entry. New code should use MALLCTL_ARENAS_ALL. - * - * `narenas` must be a snapshot of ctl_arenas->narenas taken while holding - * ctl_mtx; see ctl_narenas_get. - */ -static bool -ctl_arena_ind_is_deprecated_all(size_t i, unsigned narenas) { - return i == narenas; -} - -/* - * `narenas` must be a snapshot of ctl_arenas->narenas taken while holding - * ctl_mtx; see ctl_narenas_get. - */ -static bool -ctl_arena_ind_is_all(size_t i, unsigned narenas) { - return i == MALLCTL_ARENAS_ALL - || ctl_arena_ind_is_deprecated_all(i, narenas); -} - -/* - * ctl_arenas->narenas may grow concurrently (arena creation in - * ctl_arena_init); all reads must happen while ctl_mtx is held. This - * helper centralizes that requirement. - */ static unsigned -ctl_narenas_get(tsdn_t *tsdn) { - malloc_mutex_assert_owner(tsdn, &ctl_mtx); - return ctl_arenas->narenas; -} - -static unsigned -arenas_i2a_impl(size_t i, unsigned narenas, bool compat, bool validate) { +arenas_i2a_impl(size_t i, bool compat, bool validate) { unsigned a; switch (i) { @@ -1008,7 +993,7 @@ arenas_i2a_impl(size_t i, unsigned narenas, bool compat, bool validate) { a = 1; break; default: - if (compat && ctl_arena_ind_is_deprecated_all(i, narenas)) { + if (compat && i == ctl_arenas->narenas) { /* * Provide deprecated backward compatibility for * accessing the merged stats at index narenas rather @@ -1016,7 +1001,7 @@ arenas_i2a_impl(size_t i, unsigned narenas, bool compat, bool validate) { * removal in 6.0.0. */ a = 0; - } else if (validate && i >= narenas) { + } else if (validate && i >= ctl_arenas->narenas) { a = UINT_MAX; } else { /* @@ -1024,7 +1009,8 @@ arenas_i2a_impl(size_t i, unsigned narenas, bool compat, bool validate) { * more than one past the range of indices that have * initialized ctl data. */ - assert(i < narenas || (!validate && i == narenas)); + assert(i < ctl_arenas->narenas + || (!validate && i == ctl_arenas->narenas)); a = (unsigned)i + 2; } break; @@ -1034,8 +1020,8 @@ arenas_i2a_impl(size_t i, unsigned narenas, bool compat, bool validate) { } static unsigned -arenas_i2a(size_t i, unsigned narenas) { - return arenas_i2a_impl(i, narenas, true, false); +arenas_i2a(size_t i) { + return arenas_i2a_impl(i, true, false); } static ctl_arena_t * @@ -1043,9 +1029,8 @@ arenas_i_impl(tsd_t *tsd, size_t i, bool compat, bool init) { ctl_arena_t *ret; assert(!compat || !init); - unsigned narenas = ctl_narenas_get(tsd_tsdn(tsd)); - ret = ctl_arenas->arenas[arenas_i2a_impl(i, narenas, compat, false)]; + ret = ctl_arenas->arenas[arenas_i2a_impl(i, compat, false)]; if (init && ret == NULL) { if (config_stats) { struct container_s { @@ -1068,12 +1053,10 @@ arenas_i_impl(tsd_t *tsd, size_t i, bool compat, bool init) { } } ret->arena_ind = (unsigned)i; - ctl_arenas->arenas[arenas_i2a_impl(i, narenas, compat, false)] - = ret; + ctl_arenas->arenas[arenas_i2a_impl(i, compat, false)] = ret; } - assert(ret == NULL || - arenas_i2a(ret->arena_ind, narenas) == arenas_i2a(i, narenas)); + assert(ret == NULL || arenas_i2a(ret->arena_ind) == arenas_i2a(i)); return ret; } @@ -1326,8 +1309,6 @@ ctl_arena_init(tsd_t *tsd, const arena_config_t *config) { unsigned arena_ind; ctl_arena_t *ctl_arena; - malloc_mutex_assert_owner(tsd_tsdn(tsd), &ctl_mtx); - if ((ctl_arena = ql_last(&ctl_arenas->destroyed, destroyed_link)) != NULL) { ql_remove(&ctl_arenas->destroyed, ctl_arena, destroyed_link); @@ -2307,6 +2288,9 @@ CTL_RO_NL_CGEN(opt_malloc_conf_env_var, opt_malloc_conf_env_var, opt_malloc_conf_env_var, const char *) CTL_RO_NL_CGEN( je_malloc_conf, opt_malloc_conf_global_var, je_malloc_conf, const char *) +CTL_RO_NL_CGEN(je_malloc_conf_2_conf_harder, + opt_malloc_conf_global_var_2_conf_harder, je_malloc_conf_2_conf_harder, + const char *) /******************************************************************************/ @@ -2712,29 +2696,46 @@ label_return: static void arena_i_decay(tsdn_t *tsdn, unsigned arena_ind, bool all) { malloc_mutex_lock(tsdn, &ctl_mtx); - unsigned narenas = ctl_narenas_get(tsdn); + { + unsigned narenas = ctl_arenas->narenas; - /* - * Access via index narenas is deprecated, and scheduled for - * removal in 6.0.0. - */ - bool decay_all = ctl_arena_ind_is_all(arena_ind, narenas); - unsigned count = decay_all ? narenas : 1; - VARIABLE_ARRAY_UNSAFE(arena_t *, tarenas, count); + /* + * Access via index narenas is deprecated, and scheduled for + * removal in 6.0.0. + */ + if (arena_ind == MALLCTL_ARENAS_ALL || arena_ind == narenas) { + unsigned i; + VARIABLE_ARRAY_UNSAFE(arena_t *, tarenas, narenas); - if (decay_all) { - for (unsigned i = 0; i < narenas; i++) { - tarenas[i] = arena_get(tsdn, i, false); - } - } else { - assert(arena_ind < narenas); - tarenas[0] = arena_get(tsdn, arena_ind, false); - } - malloc_mutex_unlock(tsdn, &ctl_mtx); + for (i = 0; i < narenas; i++) { + tarenas[i] = arena_get(tsdn, i, false); + } - for (unsigned i = 0; i < count; i++) { - if (tarenas[i] != NULL) { - arena_decay(tsdn, tarenas[i], false, all); + /* + * No further need to hold ctl_mtx, since narenas and + * tarenas contain everything needed below. + */ + malloc_mutex_unlock(tsdn, &ctl_mtx); + + for (i = 0; i < narenas; i++) { + if (tarenas[i] != NULL) { + arena_decay( + tsdn, tarenas[i], false, all); + } + } + } else { + arena_t *tarena; + + assert(arena_ind < narenas); + + tarena = arena_get(tsdn, arena_ind, false); + + /* No further need to hold ctl_mtx. */ + malloc_mutex_unlock(tsdn, &ctl_mtx); + + if (tarena != NULL) { + arena_decay(tsdn, tarena, false, all); + } } } } @@ -2919,8 +2920,8 @@ arena_i_dss_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, * 6.0.0. */ dss_prec_t dss_prec_old; - unsigned narenas = ctl_narenas_get(tsd_tsdn(tsd)); - if (ctl_arena_ind_is_all(arena_ind, narenas)) { + if (arena_ind == MALLCTL_ARENAS_ALL + || arena_ind == ctl_arenas->narenas) { if (dss_prec != dss_prec_limit && extent_dss_prec_set(dss_prec)) { ret = EFAULT; @@ -2995,7 +2996,7 @@ arena_i_decay_ms_ctl_impl(tsd_t *tsd, const size_t *mib, size_t miblen, extent_state_t state = dirty ? extent_state_dirty : extent_state_muzzy; if (oldp != NULL && oldlenp != NULL) { - ssize_t oldval = arena_decay_ms_get(arena, state); + size_t oldval = arena_decay_ms_get(arena, state); READ(oldval, ssize_t); } if (newp != NULL) { @@ -3140,9 +3141,8 @@ arena_i_name_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); MIB_UNSIGNED(arena_ind, 1); - unsigned narenas = ctl_narenas_get(tsd_tsdn(tsd)); - if (ctl_arena_ind_is_all(arena_ind, narenas) - || arena_ind > narenas) { + if (arena_ind == MALLCTL_ARENAS_ALL + || arena_ind >= ctl_arenas->narenas) { ret = EINVAL; goto label_return; } @@ -3191,7 +3191,7 @@ arena_i_index(tsdn_t *tsdn, const size_t *mib, size_t miblen, size_t i) { case MALLCTL_ARENAS_DESTROYED: break; default: - if (i > ctl_narenas_get(tsdn)) { + if (i > ctl_arenas->narenas) { ret = NULL; goto label_return; } @@ -3214,7 +3214,7 @@ arenas_narenas_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); READONLY(); - narenas = ctl_narenas_get(tsd_tsdn(tsd)); + narenas = ctl_arenas->narenas; READ(narenas, unsigned); ret = 0; @@ -3229,8 +3229,8 @@ arenas_decay_ms_ctl_impl(tsd_t *tsd, const size_t *mib, size_t miblen, int ret; if (oldp != NULL && oldlenp != NULL) { - ssize_t oldval = (dirty ? arena_dirty_decay_ms_default_get() - : arena_muzzy_decay_ms_default_get()); + size_t oldval = (dirty ? arena_dirty_decay_ms_default_get() + : arena_muzzy_decay_ms_default_get()); READ(oldval, ssize_t); } if (newp != NULL) { @@ -3295,35 +3295,24 @@ arenas_lextent_i_index( return super_arenas_lextent_i_node; } -static int -ctl_arena_create(tsd_t *tsd, void *oldp, size_t *oldlenp, - const arena_config_t *config) { - int ret; - unsigned arena_ind; - - if ((arena_ind = ctl_arena_init(tsd, config)) == UINT_MAX) { - ret = EAGAIN; - goto label_return; - } - READ(arena_ind, unsigned); - - ret = 0; -label_return: - return ret; -} - static int arenas_create_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; + int ret; + unsigned arena_ind; malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); VERIFY_READ(unsigned); arena_config_t config = arena_config_default; WRITE(config.extent_hooks, extent_hooks_t *); + if ((arena_ind = ctl_arena_init(tsd, &config)) == UINT_MAX) { + ret = EAGAIN; + goto label_return; + } + READ(arena_ind, unsigned); - ret = ctl_arena_create(tsd, oldp, oldlenp, &config); + ret = 0; label_return: malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); return ret; @@ -3332,7 +3321,8 @@ label_return: static int experimental_arenas_create_ext_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; + int ret; + unsigned arena_ind; malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); @@ -3340,7 +3330,12 @@ experimental_arenas_create_ext_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, VERIFY_READ(unsigned); WRITE(config, arena_config_t); - ret = ctl_arena_create(tsd, oldp, oldlenp, &config); + if ((arena_ind = ctl_arena_init(tsd, &config)) == UINT_MAX) { + ret = EAGAIN; + goto label_return; + } + READ(arena_ind, unsigned); + ret = 0; label_return: malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); return ret; @@ -3719,6 +3714,27 @@ label_return: return ret; } +/* For integration test purpose only. No plan to move out of experimental. */ +static int +experimental_hooks_safety_check_abort_ctl(tsd_t *tsd, const size_t *mib, + size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { + int ret; + + WRITEONLY(); + if (newp != NULL) { + if (newlen != sizeof(safety_check_abort_hook_t)) { + ret = EINVAL; + goto label_return; + } + safety_check_abort_hook_t hook JEMALLOC_CC_SILENCE_INIT(NULL); + WRITE(hook, safety_check_abort_hook_t); + safety_check_set_abort(hook); + } + ret = 0; +label_return: + return ret; +} + /******************************************************************************/ CTL_RO_CGEN(config_stats, stats_allocated, ctl_stats->allocated, size_t) @@ -4238,8 +4254,8 @@ stats_arenas_i_hpa_shard_alloc_j_index( } static bool -ctl_arenas_i_verify(size_t i, unsigned narenas) { - size_t a = arenas_i2a_impl(i, narenas, true, true); +ctl_arenas_i_verify(size_t i) { + size_t a = arenas_i2a_impl(i, true, true); if (a == UINT_MAX || !ctl_arenas->arenas[a]->initialized) { return true; } @@ -4252,7 +4268,7 @@ stats_arenas_i_index(tsdn_t *tsdn, const size_t *mib, size_t miblen, size_t i) { const ctl_named_node_t *ret; malloc_mutex_lock(tsdn, &ctl_mtx); - if (ctl_arenas_i_verify(i, ctl_narenas_get(tsdn))) { + if (ctl_arenas_i_verify(i)) { ret = NULL; goto label_return; } @@ -4263,6 +4279,102 @@ label_return: return ret; } +/* + * Output six memory utilization entries for an input pointer, the first one of + * type (void *) and the remaining five of type size_t, describing the following + * (in the same order): + * + * (a) memory address of the extent a potential reallocation would go into, + * == the five fields below describe about the extent the pointer resides in == + * (b) number of free regions in the extent, + * (c) number of regions in the extent, + * (d) size of the extent in terms of bytes, + * (e) total number of free regions in the bin the extent belongs to, and + * (f) total number of regions in the bin the extent belongs to. + * + * Note that "(e)" and "(f)" are only available when stats are enabled; + * otherwise their values are undefined. + * + * This API is mainly intended for small class allocations, where extents are + * used as slab. Note that if the bin the extent belongs to is completely + * full, "(a)" will be NULL. + * + * In case of large class allocations, "(a)" will be NULL, and "(e)" and "(f)" + * will be zero (if stats are enabled; otherwise undefined). The other three + * fields will be properly set though the values are trivial: "(b)" will be 0, + * "(c)" will be 1, and "(d)" will be the usable size. + * + * The input pointer and size are respectively passed in by newp and newlen, + * and the output fields and size are respectively oldp and *oldlenp. + * + * It can be beneficial to define the following macros to make it easier to + * access the output: + * + * #define SLABCUR_READ(out) (*(void **)out) + * #define COUNTS(out) ((size_t *)((void **)out + 1)) + * #define NFREE_READ(out) COUNTS(out)[0] + * #define NREGS_READ(out) COUNTS(out)[1] + * #define SIZE_READ(out) COUNTS(out)[2] + * #define BIN_NFREE_READ(out) COUNTS(out)[3] + * #define BIN_NREGS_READ(out) COUNTS(out)[4] + * + * and then write e.g. NFREE_READ(oldp) to fetch the output. See the unit test + * test_query in test/unit/extent_util.c for an example. + * + * For a typical defragmentation workflow making use of this API for + * understanding the fragmentation level, please refer to the comment for + * experimental_utilization_batch_query_ctl. + * + * It's up to the application how to determine the significance of + * fragmentation relying on the outputs returned. Possible choices are: + * + * (a) if extent utilization ratio is below certain threshold, + * (b) if extent memory consumption is above certain threshold, + * (c) if extent utilization ratio is significantly below bin utilization ratio, + * (d) if input pointer deviates a lot from potential reallocation address, or + * (e) some selection/combination of the above. + * + * The caller needs to make sure that the input/output arguments are valid, + * in particular, that the size of the output is correct, i.e.: + * + * *oldlenp = sizeof(void *) + sizeof(size_t) * 5 + * + * Otherwise, the function immediately returns EINVAL without touching anything. + * + * In the rare case where there's no associated extent found for the input + * pointer, the function zeros out all output fields and return. Please refer + * to the comment for experimental_utilization_batch_query_ctl to understand the + * motivation from C++. + */ +static int +experimental_utilization_query_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, + void *oldp, size_t *oldlenp, void *newp, size_t newlen) { + int ret; + + assert(sizeof(inspect_extent_util_stats_verbose_t) + == sizeof(void *) + sizeof(size_t) * 5); + + if (oldp == NULL || oldlenp == NULL + || *oldlenp != sizeof(inspect_extent_util_stats_verbose_t) + || newp == NULL) { + ret = EINVAL; + goto label_return; + } + + void *ptr = NULL; + WRITE(ptr, void *); + inspect_extent_util_stats_verbose_t *util_stats = + (inspect_extent_util_stats_verbose_t *)oldp; + inspect_extent_util_stats_verbose_get(tsd_tsdn(tsd), ptr, + &util_stats->nfree, &util_stats->nregs, &util_stats->size, + &util_stats->bin_nfree, &util_stats->bin_nregs, + &util_stats->slabcur_addr); + ret = 0; + +label_return: + return ret; +} + /* * Given an input array of pointers, output three memory utilization entries of * type size_t for each input pointer about the extent it resides in: @@ -4389,6 +4501,59 @@ label_return: return ret; } +static const ctl_named_node_t * +experimental_arenas_i_index( + tsdn_t *tsdn, const size_t *mib, size_t miblen, size_t i) { + const ctl_named_node_t *ret; + + malloc_mutex_lock(tsdn, &ctl_mtx); + if (ctl_arenas_i_verify(i)) { + ret = NULL; + goto label_return; + } + ret = super_experimental_arenas_i_node; +label_return: + malloc_mutex_unlock(tsdn, &ctl_mtx); + return ret; +} + +static int +experimental_arenas_i_pactivep_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, + void *oldp, size_t *oldlenp, void *newp, size_t newlen) { + if (!config_stats) { + return ENOENT; + } + if (oldp == NULL || oldlenp == NULL || *oldlenp != sizeof(size_t *)) { + return EINVAL; + } + + unsigned arena_ind; + arena_t *arena; + int ret; + size_t *pactivep; + + malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); + READONLY(); + MIB_UNSIGNED(arena_ind, 2); + if (arena_ind < narenas_total_get() + && (arena = arena_get(tsd_tsdn(tsd), arena_ind, false)) != NULL) { +#if defined(JEMALLOC_GCC_ATOMIC_ATOMICS) || defined(JEMALLOC_GCC_SYNC_ATOMICS) \ + || defined(_MSC_VER) + /* Expose the underlying counter for fast read. */ + pactivep = (size_t *)&(arena->pa_shard.nactive.repr); + READ(pactivep, size_t *); + ret = 0; +#else + ret = EFAULT; +#endif + } else { + ret = EFAULT; + } +label_return: + malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); + return ret; +} + static int experimental_prof_recent_alloc_max_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { diff --git a/src/inspect.c b/src/inspect.c index f46bc9ef..1c0de129 100644 --- a/src/inspect.c +++ b/src/inspect.c @@ -24,3 +24,54 @@ inspect_extent_util_stats_get( assert(*nfree * edata_usize_get(edata) <= *size); } } + +void +inspect_extent_util_stats_verbose_get(tsdn_t *tsdn, const void *ptr, + size_t *nfree, size_t *nregs, size_t *size, size_t *bin_nfree, + size_t *bin_nregs, void **slabcur_addr) { + assert(ptr != NULL && nfree != NULL && nregs != NULL && size != NULL + && bin_nfree != NULL && bin_nregs != NULL && slabcur_addr != NULL); + + const edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr); + if (unlikely(edata == NULL)) { + *nfree = *nregs = *size = *bin_nfree = *bin_nregs = 0; + *slabcur_addr = NULL; + return; + } + + *size = edata_size_get(edata); + if (!edata_slab_get(edata)) { + *nfree = *bin_nfree = *bin_nregs = 0; + *nregs = 1; + *slabcur_addr = NULL; + return; + } + + *nfree = edata_nfree_get(edata); + const szind_t szind = edata_szind_get(edata); + *nregs = bin_infos[szind].nregs; + assert(*nfree <= *nregs); + assert(*nfree * edata_usize_get(edata) <= *size); + + arena_t *arena = arena_get_from_edata(edata); + assert(arena != NULL); + const unsigned binshard = edata_binshard_get(edata); + bin_t *bin = arena_get_bin(arena, szind, binshard); + + malloc_mutex_lock(tsdn, &bin->lock); + if (config_stats) { + *bin_nregs = *nregs * bin->stats.curslabs; + assert(*bin_nregs >= bin->stats.curregs); + *bin_nfree = *bin_nregs - bin->stats.curregs; + } else { + *bin_nfree = *bin_nregs = 0; + } + edata_t *slab; + if (bin->slabcur != NULL) { + slab = bin->slabcur; + } else { + slab = edata_heap_first(&bin->slabs_nonfull); + } + *slabcur_addr = slab != NULL ? edata_addr_get(slab) : NULL; + malloc_mutex_unlock(tsdn, &bin->lock); +} diff --git a/src/safety_check.c b/src/safety_check.c index cdb6f79e..d052718d 100644 --- a/src/safety_check.c +++ b/src/safety_check.c @@ -1,6 +1,8 @@ #include "jemalloc/internal/jemalloc_preamble.h" #include "jemalloc/internal/jemalloc_internal_includes.h" +static safety_check_abort_hook_t safety_check_abort; + void safety_check_fail_sized_dealloc(bool current_dealloc, const void *ptr, size_t true_size, size_t input_size) { @@ -17,18 +19,23 @@ safety_check_fail_sized_dealloc(bool current_dealloc, const void *ptr, true_size, input_size, ptr, src, suggest_debug_build); } +void +safety_check_set_abort(safety_check_abort_hook_t abort_fn) { + safety_check_abort = abort_fn; +} + /* * In addition to malloc_write, also embed hint msg in the abort function name * because there are cases only logging crash stack traces. */ static void safety_check_detected_heap_corruption___run_address_sanitizer_build_to_debug( - const char *buf) { - if (test_hooks_safety_check_abort == NULL) { + const char *buf) { + if (safety_check_abort == NULL) { malloc_write(buf); abort(); } else { - test_hooks_safety_check_abort(buf); + safety_check_abort(buf); } } diff --git a/src/stats.c b/src/stats.c index 5fa37529..65583393 100644 --- a/src/stats.c +++ b/src/stats.c @@ -1708,9 +1708,10 @@ stats_general_print(emitter_t *emitter) { MALLOC_CONF_WRITE("global_var", "Global variable malloc_conf"); MALLOC_CONF_WRITE("symlink", "Symbolic link malloc.conf"); MALLOC_CONF_WRITE("env_var", "Environment variable MALLOC_CONF"); - /* As this config is unofficial, skip the output if it's NULL. */ - if (je_malloc_conf_2_conf_harder != NULL) { - cpv = je_malloc_conf_2_conf_harder; + /* As this config is unofficial, skip the output if it's NULL */ + if (je_mallctl("opt.malloc_conf.global_var_2_conf_harder", (void *)&cpv, + &cpsz, NULL, 0) + == 0) { emitter_kv(emitter, "global_var_2_conf_harder", "Global " "variable malloc_conf_2_conf_harder", diff --git a/src/test_hooks.c b/src/test_hooks.c index 0f4c4cf7..40621199 100644 --- a/src/test_hooks.c +++ b/src/test_hooks.c @@ -10,6 +10,3 @@ void (*test_hooks_arena_new_hook)(void) = NULL; JEMALLOC_EXPORT void (*test_hooks_libc_hook)(void) = NULL; - -JEMALLOC_EXPORT -void (*test_hooks_safety_check_abort)(const char *) = NULL; diff --git a/test/integration/cpp/infallible_new_true.cpp b/test/integration/cpp/infallible_new_true.cpp index 8c011b8f..300bdd85 100644 --- a/test/integration/cpp/infallible_new_true.cpp +++ b/test/integration/cpp/infallible_new_true.cpp @@ -3,9 +3,10 @@ #include "test/jemalloc_test.h" /* - * We can't test C++ in unit tests. In order to intercept abort, use the - * internal test hook in integration tests. + * We can't test C++ in unit tests. In order to intercept abort, use a secret + * safety check abort hook in integration tests. */ +typedef void (*abort_hook_t)(const char *message); bool fake_abort_called; void fake_abort(const char *message) { @@ -33,7 +34,10 @@ own_operator_new(void) { } TEST_BEGIN(test_failing_alloc) { - test_hooks_safety_check_abort = &fake_abort; + abort_hook_t abort_hook = &fake_abort; + expect_d_eq(mallctl("experimental.hooks.safety_check_abort", NULL, NULL, + (void *)&abort_hook, sizeof(abort_hook)), + 0, "Unexpected mallctl failure setting abort hook"); /* * Not owning operator new is only expected to happen on MinGW which @@ -53,7 +57,6 @@ TEST_BEGIN(test_failing_alloc) { } expect_ptr_null(ptr, "Allocation should have failed"); expect_b_eq(fake_abort_called, true, "Abort hook not invoked"); - test_hooks_safety_check_abort = NULL; } TEST_END diff --git a/test/src/test.c b/test/src/test.c index 443a9ac3..c048a541 100644 --- a/test/src/test.c +++ b/test/src/test.c @@ -150,7 +150,6 @@ p_test_impl(bool do_malloc_init, bool do_reentrant, test_t *t, va_list ap) { /* Non-reentrant run. */ reentrancy = non_reentrant; test_hooks_arena_new_hook = test_hooks_libc_hook = NULL; - test_hooks_safety_check_abort = NULL; t(); if (test_status > ret) { ret = test_status; @@ -159,7 +158,6 @@ p_test_impl(bool do_malloc_init, bool do_reentrant, test_t *t, va_list ap) { if (do_reentrant) { reentrancy = libc_reentrant; test_hooks_arena_new_hook = NULL; - test_hooks_safety_check_abort = NULL; test_hooks_libc_hook = &libc_reentrancy_hook; t(); if (test_status > ret) { @@ -168,7 +166,6 @@ p_test_impl(bool do_malloc_init, bool do_reentrant, test_t *t, va_list ap) { reentrancy = arena_new_reentrant; test_hooks_libc_hook = NULL; - test_hooks_safety_check_abort = NULL; test_hooks_arena_new_hook = &arena_new_reentrancy_hook; t(); if (test_status > ret) { diff --git a/test/test.sh.in b/test/test.sh.in index 8eca840a..dc13bc28 100644 --- a/test/test.sh.in +++ b/test/test.sh.in @@ -2,30 +2,15 @@ case @abi@ in macho) - export DYLD_FALLBACK_LIBRARY_PATH="@abs_objroot@lib" + export DYLD_FALLBACK_LIBRARY_PATH="@objroot@lib" ;; pecoff) - export PATH="@abs_objroot@lib:${PATH}" + export PATH="${PATH}:@objroot@lib" ;; *) ;; esac -prepare_test_exec() { - case @abi@ in - pecoff) - test_dir=`dirname "$1"` - for dll in @abs_objroot@lib/*.dll ; do - if [ -f "${dll}" ] ; then - cp -f "${dll}" "${test_dir}/" - fi - done - ;; - *) - ;; - esac -} - # Make a copy of the @JEMALLOC_CPREFIX@MALLOC_CONF passed in to this script, so # it can be repeatedly concatenated with per test settings. export MALLOC_CONF_ALL=${@JEMALLOC_CPREFIX@MALLOC_CONF} @@ -60,12 +45,10 @@ for t in $@; do enable_prof=@enable_prof@ \ disable_large_size_classes=@disable_large_size_classes@ \ . @srcroot@${t}.sh && \ - prepare_test_exec ${t}@exe@ && \ export_malloc_conf && \ $JEMALLOC_TEST_PREFIX ${t}@exe@ @abs_srcroot@ @abs_objroot@ else export MALLOC_CONF= && \ - prepare_test_exec ${t}@exe@ && \ export_malloc_conf && \ $JEMALLOC_TEST_PREFIX ${t}@exe@ @abs_srcroot@ @abs_objroot@ fi diff --git a/test/unit/double_free.c b/test/unit/double_free.c index 07f28dfe..4bd6ab73 100644 --- a/test/unit/double_free.c +++ b/test/unit/double_free.c @@ -12,14 +12,14 @@ fake_abort(const char *message) { static void test_double_free_pre(void) { - test_hooks_safety_check_abort = &fake_abort; + safety_check_set_abort(&fake_abort); fake_abort_called = false; } static void test_double_free_post(void) { expect_b_eq(fake_abort_called, true, "Double-free check didn't fire."); - test_hooks_safety_check_abort = NULL; + safety_check_set_abort(NULL); } static bool diff --git a/test/unit/inspect.c b/test/unit/inspect.c index cb1cd979..8111e4a5 100644 --- a/test/unit/inspect.c +++ b/test/unit/inspect.c @@ -11,6 +11,8 @@ "Output content touched when given invalid arguments"); \ } while (0) +#define TEST_UTIL_QUERY_EINVAL(a, b, c, d, why_inval) \ + TEST_UTIL_EINVAL("query", a, b, c, d, why_inval) #define TEST_UTIL_BATCH_EINVAL(a, b, c, d, why_inval) \ TEST_UTIL_EINVAL("batch_query", a, b, c, d, why_inval) @@ -28,6 +30,139 @@ #define TEST_MAX_SIZE (1 << 20) +TEST_BEGIN(test_query) { + size_t sz; + /* + * Select some sizes that can span both small and large sizes, and are + * numerically unrelated to any size boundaries. + */ + for (sz = 7; sz <= TEST_MAX_SIZE && sz <= SC_LARGE_MAXCLASS; + sz += (sz <= SC_SMALL_MAXCLASS ? 1009 : 99989)) { + void *p = mallocx(sz, 0); + void **in = &p; + size_t in_sz = sizeof(const void *); + size_t out_sz = sizeof(void *) + sizeof(size_t) * 5; + void *out = mallocx(out_sz, 0); + void *out_ref = mallocx(out_sz, 0); + size_t out_sz_ref = out_sz; + + assert_ptr_not_null(p, "test pointer allocation failed"); + assert_ptr_not_null(out, "test output allocation failed"); + assert_ptr_not_null( + out_ref, "test reference output allocation failed"); + +#define SLABCUR_READ(out) (*(void **)out) +#define COUNTS(out) ((size_t *)((void **)out + 1)) +#define NFREE_READ(out) COUNTS(out)[0] +#define NREGS_READ(out) COUNTS(out)[1] +#define SIZE_READ(out) COUNTS(out)[2] +#define BIN_NFREE_READ(out) COUNTS(out)[3] +#define BIN_NREGS_READ(out) COUNTS(out)[4] + + SLABCUR_READ(out) = NULL; + NFREE_READ(out) = NREGS_READ(out) = SIZE_READ(out) = -1; + BIN_NFREE_READ(out) = BIN_NREGS_READ(out) = -1; + memcpy(out_ref, out, out_sz); + + /* Test invalid argument(s) errors */ + TEST_UTIL_QUERY_EINVAL(NULL, &out_sz, in, in_sz, "old is NULL"); + TEST_UTIL_QUERY_EINVAL(out, NULL, in, in_sz, "oldlenp is NULL"); + TEST_UTIL_QUERY_EINVAL( + out, &out_sz, NULL, in_sz, "newp is NULL"); + TEST_UTIL_QUERY_EINVAL(out, &out_sz, in, 0, "newlen is zero"); + in_sz -= 1; + TEST_UTIL_QUERY_EINVAL( + out, &out_sz, in, in_sz, "invalid newlen"); + in_sz += 1; + out_sz_ref = out_sz -= 2 * sizeof(size_t); + TEST_UTIL_QUERY_EINVAL( + out, &out_sz, in, in_sz, "invalid *oldlenp"); + out_sz_ref = out_sz += 2 * sizeof(size_t); + + /* Examine output for valid call */ + TEST_UTIL_VALID("query"); + expect_zu_le(sz, SIZE_READ(out), + "Extent size should be at least allocation size"); + expect_zu_eq(SIZE_READ(out) & (PAGE - 1), 0, + "Extent size should be a multiple of page size"); + + /* + * We don't do much bin checking if prof is on, since profiling + * can produce extents that are for small size classes but not + * slabs, which interferes with things like region counts. + */ + if (!opt_prof && sz <= SC_SMALL_MAXCLASS) { + expect_zu_le(NFREE_READ(out), NREGS_READ(out), + "Extent free count exceeded region count"); + expect_zu_le(NREGS_READ(out), SIZE_READ(out), + "Extent region count exceeded size"); + expect_zu_ne(NREGS_READ(out), 0, + "Extent region count must be positive"); + expect_true(NFREE_READ(out) == 0 + || (SLABCUR_READ(out) != NULL + && SLABCUR_READ(out) <= p), + "Allocation should follow first fit principle"); + + if (config_stats) { + expect_zu_le(BIN_NFREE_READ(out), + BIN_NREGS_READ(out), + "Bin free count exceeded region count"); + expect_zu_ne(BIN_NREGS_READ(out), 0, + "Bin region count must be positive"); + expect_zu_le(NFREE_READ(out), + BIN_NFREE_READ(out), + "Extent free count exceeded bin free count"); + expect_zu_le(NREGS_READ(out), + BIN_NREGS_READ(out), + "Extent region count exceeded " + "bin region count"); + expect_zu_eq( + BIN_NREGS_READ(out) % NREGS_READ(out), 0, + "Bin region count isn't a multiple of " + "extent region count"); + expect_zu_le( + BIN_NFREE_READ(out) - NFREE_READ(out), + BIN_NREGS_READ(out) - NREGS_READ(out), + "Free count in other extents in the bin " + "exceeded region count in other extents " + "in the bin"); + expect_zu_le(NREGS_READ(out) - NFREE_READ(out), + BIN_NREGS_READ(out) - BIN_NFREE_READ(out), + "Extent utilized count exceeded " + "bin utilized count"); + } + } else if (sz > SC_SMALL_MAXCLASS) { + expect_zu_eq(NFREE_READ(out), 0, + "Extent free count should be zero"); + expect_zu_eq(NREGS_READ(out), 1, + "Extent region count should be one"); + expect_ptr_null(SLABCUR_READ(out), + "Current slab must be null for large size classes"); + if (config_stats) { + expect_zu_eq(BIN_NFREE_READ(out), 0, + "Bin free count must be zero for " + "large sizes"); + expect_zu_eq(BIN_NREGS_READ(out), 0, + "Bin region count must be zero for " + "large sizes"); + } + } + +#undef BIN_NREGS_READ +#undef BIN_NFREE_READ +#undef SIZE_READ +#undef NREGS_READ +#undef NFREE_READ +#undef COUNTS +#undef SLABCUR_READ + + free(out_ref); + free(out); + free(p); + } +} +TEST_END + TEST_BEGIN(test_batch) { size_t sz; /* @@ -82,7 +217,10 @@ TEST_BEGIN(test_batch) { "Extent size should be at least allocation size"); expect_zu_eq(SIZE_READ(out, 0) & (PAGE - 1), 0, "Extent size should be a multiple of page size"); - /* Profiling breaks our slab count expectations. */ + /* + * See the corresponding comment in test_query; profiling breaks + * our slab count expectations. + */ if (sz <= SC_SMALL_MAXCLASS && !opt_prof) { expect_zu_le(NFREE_READ(out, 0), NREGS_READ(out, 0), "Extent free count exceeded region count"); @@ -132,5 +270,5 @@ int main(void) { assert_zu_lt(SC_SMALL_MAXCLASS + 100000, TEST_MAX_SIZE, "Test case cannot cover large classes"); - return test(test_batch); + return test(test_query, test_batch); } diff --git a/test/unit/malloc_conf_2.c b/test/unit/malloc_conf_2.c index eb6856c8..667e7006 100644 --- a/test/unit/malloc_conf_2.c +++ b/test/unit/malloc_conf_2.c @@ -39,6 +39,13 @@ TEST_BEGIN(test_mallctl_global_var) { expect_str_eq(mc, malloc_conf, "Unexpected value for the global variable " "malloc_conf"); + + expect_d_eq(mallctl("opt.malloc_conf.global_var_2_conf_harder", + (void *)&mc, &sz, NULL, 0), + 0, "Unexpected mallctl() failure"); + expect_str_eq(mc, malloc_conf_2_conf_harder, + "Unexpected value for the " + "global variable malloc_conf_2_conf_harder"); } TEST_END diff --git a/test/unit/prof_hook.c b/test/unit/prof_hook.c index 8217f309..1d58469c 100644 --- a/test/unit/prof_hook.c +++ b/test/unit/prof_hook.c @@ -334,50 +334,9 @@ TEST_BEGIN(test_prof_sample_hooks) { } TEST_END -TEST_BEGIN(test_prof_hook_noop) { - test_skip_if(!config_prof); - - const char *hooks[] = {"experimental.hooks.prof_backtrace", - "experimental.hooks.prof_dump", "experimental.hooks.prof_sample", - "experimental.hooks.prof_sample_free"}; - - /* Passing NULL for both oldp and newp must always return EINVAL. */ - for (unsigned i = 0; i < sizeof(hooks) / sizeof(hooks[0]); i++) { - expect_d_eq(mallctl(hooks[i], NULL, NULL, NULL, 0), EINVAL, - "Unexpected noop hook mallctl result"); - } - - /* - * Passing a pointer to a NULL function pointer: - * prof_backtrace requires a non-NULL hook -> EINVAL (unchanged) - * prof_dump / prof_sample / prof_sample_free allow unsetting -> 0 - * - * For the latter, save and restore the prior value so this test - * doesn't leak NULL hook state to any future test. - */ - prof_backtrace_hook_t bt_null = NULL; - expect_d_eq(mallctl(hooks[0], NULL, NULL, &bt_null, sizeof(bt_null)), - EINVAL, "Incorrectly allowed NULL backtrace hook"); - - for (unsigned i = 1; i < sizeof(hooks) / sizeof(hooks[0]); i++) { - void *saved = NULL; - size_t saved_sz = sizeof(saved); - void *new_hook = NULL; - /* Read current value into `saved`, write NULL in same call. */ - expect_d_eq(mallctl(hooks[i], &saved, &saved_sz, - &new_hook, sizeof(new_hook)), - 0, "Unexpected null-hook mallctl result"); - /* Restore. */ - expect_d_eq(mallctl(hooks[i], NULL, NULL, - &saved, sizeof(saved)), - 0, "Failed to restore hook"); - } -} -TEST_END - int main(void) { return test(test_prof_backtrace_hook_replace, test_prof_backtrace_hook_augment, test_prof_dump_hook, - test_prof_sample_hooks, test_prof_hook_noop); + test_prof_sample_hooks); } diff --git a/test/unit/safety_check.c b/test/unit/safety_check.c index 24dd3fd8..558797c0 100644 --- a/test/unit/safety_check.c +++ b/test/unit/safety_check.c @@ -25,12 +25,12 @@ TEST_BEGIN(test_malloc_free_overflow) { test_skip_if(!config_prof); test_skip_if(!config_opt_safety_checks); - test_hooks_safety_check_abort = &fake_abort; + safety_check_set_abort(&fake_abort); /* Buffer overflow! */ char *ptr = malloc(128); buffer_overflow_write(ptr, 128); free(ptr); - test_hooks_safety_check_abort = NULL; + safety_check_set_abort(NULL); expect_b_eq(fake_abort_called, true, "Redzone check didn't fire."); fake_abort_called = false; @@ -41,12 +41,12 @@ TEST_BEGIN(test_mallocx_dallocx_overflow) { test_skip_if(!config_prof); test_skip_if(!config_opt_safety_checks); - test_hooks_safety_check_abort = &fake_abort; + safety_check_set_abort(&fake_abort); /* Buffer overflow! */ char *ptr = mallocx(128, 0); buffer_overflow_write(ptr, 128); dallocx(ptr, 0); - test_hooks_safety_check_abort = NULL; + safety_check_set_abort(NULL); expect_b_eq(fake_abort_called, true, "Redzone check didn't fire."); fake_abort_called = false; @@ -57,12 +57,12 @@ TEST_BEGIN(test_malloc_sdallocx_overflow) { test_skip_if(!config_prof); test_skip_if(!config_opt_safety_checks); - test_hooks_safety_check_abort = &fake_abort; + safety_check_set_abort(&fake_abort); /* Buffer overflow! */ char *ptr = malloc(128); buffer_overflow_write(ptr, 128); sdallocx(ptr, 128, 0); - test_hooks_safety_check_abort = NULL; + safety_check_set_abort(NULL); expect_b_eq(fake_abort_called, true, "Redzone check didn't fire."); fake_abort_called = false; @@ -73,12 +73,12 @@ TEST_BEGIN(test_realloc_overflow) { test_skip_if(!config_prof); test_skip_if(!config_opt_safety_checks); - test_hooks_safety_check_abort = &fake_abort; + safety_check_set_abort(&fake_abort); /* Buffer overflow! */ char *ptr = malloc(128); buffer_overflow_write(ptr, 128); ptr = realloc(ptr, 129); - test_hooks_safety_check_abort = NULL; + safety_check_set_abort(NULL); free(ptr); expect_b_eq(fake_abort_called, true, "Redzone check didn't fire."); @@ -90,12 +90,12 @@ TEST_BEGIN(test_rallocx_overflow) { test_skip_if(!config_prof); test_skip_if(!config_opt_safety_checks); - test_hooks_safety_check_abort = &fake_abort; + safety_check_set_abort(&fake_abort); /* Buffer overflow! */ char *ptr = malloc(128); buffer_overflow_write(ptr, 128); ptr = rallocx(ptr, 129, 0); - test_hooks_safety_check_abort = NULL; + safety_check_set_abort(NULL); free(ptr); expect_b_eq(fake_abort_called, true, "Redzone check didn't fire."); @@ -107,7 +107,7 @@ TEST_BEGIN(test_xallocx_overflow) { test_skip_if(!config_prof); test_skip_if(!config_opt_safety_checks); - test_hooks_safety_check_abort = &fake_abort; + safety_check_set_abort(&fake_abort); /* Buffer overflow! */ char *ptr = malloc(128); buffer_overflow_write(ptr, 128); @@ -116,7 +116,7 @@ TEST_BEGIN(test_xallocx_overflow) { free(ptr); expect_b_eq(fake_abort_called, true, "Redzone check didn't fire."); fake_abort_called = false; - test_hooks_safety_check_abort = NULL; + safety_check_set_abort(NULL); } TEST_END diff --git a/test/unit/size_check.c b/test/unit/size_check.c index 2fe3733b..a31578bf 100644 --- a/test/unit/size_check.c +++ b/test/unit/size_check.c @@ -17,7 +17,7 @@ fake_abort(const char *message) { static void * test_invalid_size_pre(size_t sz) { - test_hooks_safety_check_abort = &fake_abort; + safety_check_set_abort(&fake_abort); fake_abort_called = false; void *ptr = malloc(sz); @@ -29,7 +29,7 @@ test_invalid_size_pre(size_t sz) { static void test_invalid_size_post(void) { expect_true(fake_abort_called, "Safety check didn't fire"); - test_hooks_safety_check_abort = NULL; + safety_check_set_abort(NULL); } TEST_BEGIN(test_invalid_size_sdallocx) { diff --git a/test/unit/uaf.c b/test/unit/uaf.c index b9d98803..25399ed0 100644 --- a/test/unit/uaf.c +++ b/test/unit/uaf.c @@ -19,7 +19,7 @@ fake_abort(const char *message) { static void test_write_after_free_pre(void) { - test_hooks_safety_check_abort = &fake_abort; + safety_check_set_abort(&fake_abort); fake_abort_called = false; } @@ -28,7 +28,7 @@ test_write_after_free_post(void) { assert_d_eq(mallctl("thread.tcache.flush", NULL, NULL, NULL, 0), 0, "Unexpected tcache flush failure"); expect_true(fake_abort_called, "Use-after-free check didn't fire."); - test_hooks_safety_check_abort = NULL; + safety_check_set_abort(NULL); } static bool diff --git a/test/unit/zero_realloc_abort.c b/test/unit/zero_realloc_abort.c index 03cbe16e..1d8bf9c3 100644 --- a/test/unit/zero_realloc_abort.c +++ b/test/unit/zero_realloc_abort.c @@ -12,7 +12,7 @@ set_abort_called(const char *message) { TEST_BEGIN(test_realloc_abort) { abort_called = false; - test_hooks_safety_check_abort = &set_abort_called; + safety_check_set_abort(&set_abort_called); void *ptr = mallocx(42, 0); expect_ptr_not_null(ptr, "Unexpected mallocx error"); ptr = realloc(ptr, 0);