From 533ffb67e3e8000a059bc9cc86d678b22888f550 Mon Sep 17 00:00:00 2001 From: Slobodan Predolac Date: Thu, 21 May 2026 14:11:26 -0700 Subject: [PATCH 1/8] Refactor arena ctl helpers --- src/ctl.c | 95 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 37 deletions(-) diff --git a/src/ctl.c b/src/ctl.c index e048135a..592f764b 100644 --- a/src/ctl.c +++ b/src/ctl.c @@ -981,6 +981,21 @@ ctl_accum_atomic_zu(atomic_zu_t *dst, atomic_zu_t *src) { /******************************************************************************/ +static bool +ctl_arena_ind_is_deprecated_all(size_t i, unsigned narenas) { + /* + * Historical compatibility for treating arena. as the merged + * all-arenas entry. New code should use MALLCTL_ARENAS_ALL. + */ + return i == narenas; +} + +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); +} + static unsigned arenas_i2a_impl(size_t i, bool compat, bool validate) { unsigned a; @@ -993,7 +1008,8 @@ arenas_i2a_impl(size_t i, bool compat, bool validate) { a = 1; break; default: - if (compat && i == ctl_arenas->narenas) { + if (compat && ctl_arena_ind_is_deprecated_all( + i, ctl_arenas->narenas)) { /* * Provide deprecated backward compatibility for * accessing the merged stats at index narenas rather @@ -2693,6 +2709,40 @@ label_return: return ret; } +static void +arena_i_decay_all(tsdn_t *tsdn, unsigned narenas, bool all) { + unsigned i; + VARIABLE_ARRAY_UNSAFE(arena_t *, tarenas, narenas); + + for (i = 0; i < narenas; i++) { + tarenas[i] = arena_get(tsdn, i, false); + } + + /* + * 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); + } + } +} + +static void +arena_i_decay_one(tsdn_t *tsdn, unsigned arena_ind, bool all) { + arena_t *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); + } +} + static void arena_i_decay(tsdn_t *tsdn, unsigned arena_ind, bool all) { malloc_mutex_lock(tsdn, &ctl_mtx); @@ -2703,39 +2753,11 @@ arena_i_decay(tsdn_t *tsdn, unsigned arena_ind, bool all) { * 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); - - for (i = 0; i < narenas; i++) { - tarenas[i] = arena_get(tsdn, i, false); - } - - /* - * 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); - } - } + if (ctl_arena_ind_is_all(arena_ind, narenas)) { + arena_i_decay_all(tsdn, narenas, 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); - } + arena_i_decay_one(tsdn, arena_ind, all); } } } @@ -2920,8 +2942,7 @@ 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; - if (arena_ind == MALLCTL_ARENAS_ALL - || arena_ind == ctl_arenas->narenas) { + if (ctl_arena_ind_is_all(arena_ind, ctl_arenas->narenas)) { if (dss_prec != dss_prec_limit && extent_dss_prec_set(dss_prec)) { ret = EFAULT; @@ -2996,7 +3017,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) { - size_t oldval = arena_decay_ms_get(arena, state); + ssize_t oldval = arena_decay_ms_get(arena, state); READ(oldval, ssize_t); } if (newp != NULL) { @@ -3229,8 +3250,8 @@ arenas_decay_ms_ctl_impl(tsd_t *tsd, const size_t *mib, size_t miblen, int ret; if (oldp != NULL && oldlenp != NULL) { - size_t oldval = (dirty ? arena_dirty_decay_ms_default_get() - : arena_muzzy_decay_ms_default_get()); + ssize_t oldval = (dirty ? arena_dirty_decay_ms_default_get() + : arena_muzzy_decay_ms_default_get()); READ(oldval, ssize_t); } if (newp != NULL) { From 366a2cd9c03b865b297a9e59be0a96829a1e884a Mon Sep 17 00:00:00 2001 From: Slobodan Predolac Date: Thu, 21 May 2026 14:11:40 -0700 Subject: [PATCH 2/8] Internalize malloc_conf_2_conf_harder ctl --- include/jemalloc/internal/jemalloc_internal_externs.h | 1 + src/ctl.c | 8 +------- src/stats.c | 7 +++---- test/unit/malloc_conf_2.c | 7 ------- 4 files changed, 5 insertions(+), 18 deletions(-) diff --git a/include/jemalloc/internal/jemalloc_internal_externs.h b/include/jemalloc/internal/jemalloc_internal_externs.h index b5b12e91..cd458ff8 100644 --- a/include/jemalloc/internal/jemalloc_internal_externs.h +++ b/include/jemalloc/internal/jemalloc_internal_externs.h @@ -46,6 +46,7 @@ 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/src/ctl.c b/src/ctl.c index 592f764b..2112c26e 100644 --- a/src/ctl.c +++ b/src/ctl.c @@ -173,7 +173,6 @@ 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) @@ -468,9 +467,7 @@ 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_2_conf_harder"), - CTL(opt_malloc_conf_global_var_2_conf_harder)}}; + {NAME("global_var"), CTL(opt_malloc_conf_global_var)}}; static const ctl_named_node_t opt_node[] = {{NAME("abort"), CTL(opt_abort)}, {NAME("abort_conf"), CTL(opt_abort_conf)}, @@ -2304,9 +2301,6 @@ 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 *) /******************************************************************************/ diff --git a/src/stats.c b/src/stats.c index 65583393..5fa37529 100644 --- a/src/stats.c +++ b/src/stats.c @@ -1708,10 +1708,9 @@ 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_mallctl("opt.malloc_conf.global_var_2_conf_harder", (void *)&cpv, - &cpsz, NULL, 0) - == 0) { + /* 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; emitter_kv(emitter, "global_var_2_conf_harder", "Global " "variable malloc_conf_2_conf_harder", diff --git a/test/unit/malloc_conf_2.c b/test/unit/malloc_conf_2.c index 667e7006..eb6856c8 100644 --- a/test/unit/malloc_conf_2.c +++ b/test/unit/malloc_conf_2.c @@ -39,13 +39,6 @@ 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 From bbe86b591f64493b3ed197c779c1f79bab8b1617 Mon Sep 17 00:00:00 2001 From: Slobodan Predolac Date: Thu, 21 May 2026 17:15:14 -0700 Subject: [PATCH 3/8] Deduplicate prof hook ctl handlers --- src/ctl.c | 131 +++++++++++++----------------------------- test/unit/prof_hook.c | 16 +++++- 2 files changed, 54 insertions(+), 93 deletions(-) diff --git a/src/ctl.c b/src/ctl.c index 2112c26e..132d0503 100644 --- a/src/ctl.c +++ b/src/ctl.c @@ -3599,118 +3599,65 @@ prof_log_stop_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, return 0; } +#define PROF_HOOK_CTL_BODY(hook_type, hook_get, hook_set, allow_null) \ + do { \ + int ret; \ + if (oldp == NULL && newp == NULL) { \ + ret = EINVAL; \ + goto label_return; \ + } \ + if (oldp != NULL) { \ + hook_type old_hook = hook_get(); \ + READ(old_hook, hook_type); \ + } \ + if (newp != NULL) { \ + if (!opt_prof) { \ + ret = ENOENT; \ + goto label_return; \ + } \ + hook_type new_hook JEMALLOC_CC_SILENCE_INIT(NULL); \ + WRITE(new_hook, hook_type); \ + if (!(allow_null) && new_hook == NULL) { \ + ret = EINVAL; \ + goto label_return; \ + } \ + hook_set(new_hook); \ + } \ + ret = 0; \ + label_return: \ + return ret; \ + } while (0) + static int experimental_hooks_prof_backtrace_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - - if (oldp == NULL && newp == NULL) { - ret = EINVAL; - goto label_return; - } - if (oldp != NULL) { - prof_backtrace_hook_t old_hook = prof_backtrace_hook_get(); - READ(old_hook, prof_backtrace_hook_t); - } - if (newp != NULL) { - if (!opt_prof) { - ret = ENOENT; - goto label_return; - } - prof_backtrace_hook_t new_hook JEMALLOC_CC_SILENCE_INIT(NULL); - WRITE(new_hook, prof_backtrace_hook_t); - if (new_hook == NULL) { - ret = EINVAL; - goto label_return; - } - prof_backtrace_hook_set(new_hook); - } - ret = 0; -label_return: - return ret; + PROF_HOOK_CTL_BODY(prof_backtrace_hook_t, prof_backtrace_hook_get, + prof_backtrace_hook_set, false); } static int experimental_hooks_prof_dump_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - - if (oldp == NULL && newp == NULL) { - ret = EINVAL; - goto label_return; - } - if (oldp != NULL) { - prof_dump_hook_t old_hook = prof_dump_hook_get(); - READ(old_hook, prof_dump_hook_t); - } - if (newp != NULL) { - if (!opt_prof) { - ret = ENOENT; - goto label_return; - } - prof_dump_hook_t new_hook JEMALLOC_CC_SILENCE_INIT(NULL); - WRITE(new_hook, prof_dump_hook_t); - prof_dump_hook_set(new_hook); - } - ret = 0; -label_return: - return ret; + PROF_HOOK_CTL_BODY(prof_dump_hook_t, prof_dump_hook_get, + prof_dump_hook_set, true); } static int experimental_hooks_prof_sample_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - - if (oldp == NULL && newp == NULL) { - ret = EINVAL; - goto label_return; - } - if (oldp != NULL) { - prof_sample_hook_t old_hook = prof_sample_hook_get(); - READ(old_hook, prof_sample_hook_t); - } - if (newp != NULL) { - if (!opt_prof) { - ret = ENOENT; - goto label_return; - } - prof_sample_hook_t new_hook JEMALLOC_CC_SILENCE_INIT(NULL); - WRITE(new_hook, prof_sample_hook_t); - prof_sample_hook_set(new_hook); - } - ret = 0; -label_return: - return ret; + PROF_HOOK_CTL_BODY(prof_sample_hook_t, prof_sample_hook_get, + prof_sample_hook_set, true); } static int experimental_hooks_prof_sample_free_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { - int ret; - - if (oldp == NULL && newp == NULL) { - ret = EINVAL; - goto label_return; - } - if (oldp != NULL) { - prof_sample_free_hook_t old_hook = prof_sample_free_hook_get(); - READ(old_hook, prof_sample_free_hook_t); - } - if (newp != NULL) { - if (!opt_prof) { - ret = ENOENT; - goto label_return; - } - prof_sample_free_hook_t new_hook JEMALLOC_CC_SILENCE_INIT(NULL); - WRITE(new_hook, prof_sample_free_hook_t); - prof_sample_free_hook_set(new_hook); - } - ret = 0; -label_return: - return ret; + PROF_HOOK_CTL_BODY(prof_sample_free_hook_t, prof_sample_free_hook_get, + prof_sample_free_hook_set, true); } +#undef PROF_HOOK_CTL_BODY + static int experimental_hooks_thread_event_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) { diff --git a/test/unit/prof_hook.c b/test/unit/prof_hook.c index 1d58469c..72c0c43e 100644 --- a/test/unit/prof_hook.c +++ b/test/unit/prof_hook.c @@ -334,9 +334,23 @@ 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"}; + + 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"); + } +} +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_sample_hooks, test_prof_hook_noop); } From 9cecc7bfa78ce72411689d4e516c3e8319695aef Mon Sep 17 00:00:00 2001 From: Slobodan Predolac Date: Fri, 22 May 2026 10:08:45 -0700 Subject: [PATCH 4/8] Remove safety check abort mallctl --- include/jemalloc/internal/safety_check.h | 5 ---- include/jemalloc/internal/test_hooks.h | 1 + src/ctl.c | 23 ------------------- src/safety_check.c | 13 +++-------- src/test_hooks.c | 3 +++ test/integration/cpp/infallible_new_true.cpp | 11 ++++----- test/src/test.c | 3 +++ test/unit/double_free.c | 4 ++-- test/unit/safety_check.c | 24 ++++++++++---------- test/unit/size_check.c | 4 ++-- test/unit/uaf.c | 4 ++-- test/unit/zero_realloc_abort.c | 2 +- 12 files changed, 33 insertions(+), 64 deletions(-) diff --git a/include/jemalloc/internal/safety_check.h b/include/jemalloc/internal/safety_check.h index 2b4b2d0e..360e4aa9 100644 --- a/include/jemalloc/internal/safety_check.h +++ b/include/jemalloc/internal/safety_check.h @@ -11,11 +11,6 @@ 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 35f3a211..9df15383 100644 --- a/include/jemalloc/internal/test_hooks.h +++ b/include/jemalloc/internal/test_hooks.h @@ -5,6 +5,7 @@ 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 132d0503..f522b3bd 100644 --- a/src/ctl.c +++ b/src/ctl.c @@ -372,7 +372,6 @@ 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) @@ -910,7 +909,6 @@ 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)}, }; @@ -3676,27 +3674,6 @@ 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) diff --git a/src/safety_check.c b/src/safety_check.c index d052718d..cdb6f79e 100644 --- a/src/safety_check.c +++ b/src/safety_check.c @@ -1,8 +1,6 @@ #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) { @@ -19,23 +17,18 @@ 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 (safety_check_abort == NULL) { + const char *buf) { + if (test_hooks_safety_check_abort == NULL) { malloc_write(buf); abort(); } else { - safety_check_abort(buf); + test_hooks_safety_check_abort(buf); } } diff --git a/src/test_hooks.c b/src/test_hooks.c index 40621199..0f4c4cf7 100644 --- a/src/test_hooks.c +++ b/src/test_hooks.c @@ -10,3 +10,6 @@ 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 300bdd85..8c011b8f 100644 --- a/test/integration/cpp/infallible_new_true.cpp +++ b/test/integration/cpp/infallible_new_true.cpp @@ -3,10 +3,9 @@ #include "test/jemalloc_test.h" /* - * We can't test C++ in unit tests. In order to intercept abort, use a secret - * safety check abort hook in integration tests. + * We can't test C++ in unit tests. In order to intercept abort, use the + * internal test hook in integration tests. */ -typedef void (*abort_hook_t)(const char *message); bool fake_abort_called; void fake_abort(const char *message) { @@ -34,10 +33,7 @@ own_operator_new(void) { } TEST_BEGIN(test_failing_alloc) { - 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"); + test_hooks_safety_check_abort = &fake_abort; /* * Not owning operator new is only expected to happen on MinGW which @@ -57,6 +53,7 @@ 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 c048a541..443a9ac3 100644 --- a/test/src/test.c +++ b/test/src/test.c @@ -150,6 +150,7 @@ 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; @@ -158,6 +159,7 @@ 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) { @@ -166,6 +168,7 @@ 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/unit/double_free.c b/test/unit/double_free.c index 4bd6ab73..07f28dfe 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) { - safety_check_set_abort(&fake_abort); + test_hooks_safety_check_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."); - safety_check_set_abort(NULL); + test_hooks_safety_check_abort = NULL; } static bool diff --git a/test/unit/safety_check.c b/test/unit/safety_check.c index 558797c0..24dd3fd8 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); - safety_check_set_abort(&fake_abort); + test_hooks_safety_check_abort = &fake_abort; /* Buffer overflow! */ char *ptr = malloc(128); buffer_overflow_write(ptr, 128); free(ptr); - safety_check_set_abort(NULL); + test_hooks_safety_check_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); - safety_check_set_abort(&fake_abort); + test_hooks_safety_check_abort = &fake_abort; /* Buffer overflow! */ char *ptr = mallocx(128, 0); buffer_overflow_write(ptr, 128); dallocx(ptr, 0); - safety_check_set_abort(NULL); + test_hooks_safety_check_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); - safety_check_set_abort(&fake_abort); + test_hooks_safety_check_abort = &fake_abort; /* Buffer overflow! */ char *ptr = malloc(128); buffer_overflow_write(ptr, 128); sdallocx(ptr, 128, 0); - safety_check_set_abort(NULL); + test_hooks_safety_check_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); - safety_check_set_abort(&fake_abort); + test_hooks_safety_check_abort = &fake_abort; /* Buffer overflow! */ char *ptr = malloc(128); buffer_overflow_write(ptr, 128); ptr = realloc(ptr, 129); - safety_check_set_abort(NULL); + test_hooks_safety_check_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); - safety_check_set_abort(&fake_abort); + test_hooks_safety_check_abort = &fake_abort; /* Buffer overflow! */ char *ptr = malloc(128); buffer_overflow_write(ptr, 128); ptr = rallocx(ptr, 129, 0); - safety_check_set_abort(NULL); + test_hooks_safety_check_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); - safety_check_set_abort(&fake_abort); + test_hooks_safety_check_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; - safety_check_set_abort(NULL); + test_hooks_safety_check_abort = NULL; } TEST_END diff --git a/test/unit/size_check.c b/test/unit/size_check.c index a31578bf..2fe3733b 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) { - safety_check_set_abort(&fake_abort); + test_hooks_safety_check_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"); - safety_check_set_abort(NULL); + test_hooks_safety_check_abort = NULL; } TEST_BEGIN(test_invalid_size_sdallocx) { diff --git a/test/unit/uaf.c b/test/unit/uaf.c index 25399ed0..b9d98803 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) { - safety_check_set_abort(&fake_abort); + test_hooks_safety_check_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."); - safety_check_set_abort(NULL); + test_hooks_safety_check_abort = NULL; } static bool diff --git a/test/unit/zero_realloc_abort.c b/test/unit/zero_realloc_abort.c index 1d8bf9c3..03cbe16e 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; - safety_check_set_abort(&set_abort_called); + test_hooks_safety_check_abort = &set_abort_called; void *ptr = mallocx(42, 0); expect_ptr_not_null(ptr, "Unexpected mallocx error"); ptr = realloc(ptr, 0); From c9bbaff1239f16caab818b3ac7f4933509f96a7f Mon Sep 17 00:00:00 2001 From: Slobodan Predolac Date: Fri, 22 May 2026 14:03:19 -0700 Subject: [PATCH 5/8] Deduplicate arena create ctl --- src/ctl.c | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/ctl.c b/src/ctl.c index f522b3bd..cd5bce5f 100644 --- a/src/ctl.c +++ b/src/ctl.c @@ -3309,23 +3309,34 @@ arenas_lextent_i_index( } 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; +ctl_arena_create(tsd_t *tsd, void *oldp, size_t *oldlenp, + const arena_config_t *config) { + 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) { + 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; + + 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 *); + + ret = ctl_arena_create(tsd, oldp, oldlenp, &config); label_return: malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); return ret; @@ -3334,8 +3345,7 @@ 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; - unsigned arena_ind; + int ret; malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx); @@ -3343,12 +3353,7 @@ experimental_arenas_create_ext_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, VERIFY_READ(unsigned); WRITE(config, arena_config_t); - if ((arena_ind = ctl_arena_init(tsd, &config)) == UINT_MAX) { - ret = EAGAIN; - goto label_return; - } - READ(arena_ind, unsigned); - ret = 0; + ret = ctl_arena_create(tsd, oldp, oldlenp, &config); label_return: malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx); return ret; From 39d4b20890bc55b8c45a49f68a8553a7e9d7863e Mon Sep 17 00:00:00 2001 From: Slobodan Predolac Date: Fri, 22 May 2026 17:02:18 -0700 Subject: [PATCH 6/8] Remove pactivep mallctl --- src/ctl.c | 64 ------------------------------------------------------- 1 file changed, 64 deletions(-) diff --git a/src/ctl.c b/src/ctl.c index cd5bce5f..c0a602fc 100644 --- a/src/ctl.c +++ b/src/ctl.c @@ -374,8 +374,6 @@ CTL_PROTO(experimental_hooks_prof_sample_free) CTL_PROTO(experimental_hooks_thread_event) 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) @@ -916,14 +914,6 @@ 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)}, @@ -932,7 +922,6 @@ 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)}}; @@ -4445,59 +4434,6 @@ 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) { From 44bb61e19e652e02a4395272dc32abcb49f5a473 Mon Sep 17 00:00:00 2001 From: Slobodan Predolac Date: Thu, 28 May 2026 10:54:42 -0700 Subject: [PATCH 7/8] Remove utilization query mallctl --- include/jemalloc/internal/inspect.h | 18 +--- src/ctl.c | 98 ------------------- src/inspect.c | 51 ---------- test/unit/inspect.c | 142 +--------------------------- 4 files changed, 3 insertions(+), 306 deletions(-) diff --git a/include/jemalloc/internal/inspect.h b/include/jemalloc/internal/inspect.h index e8ed44d3..d8723f96 100644 --- a/include/jemalloc/internal/inspect.h +++ b/include/jemalloc/internal/inspect.h @@ -11,8 +11,7 @@ */ /* - * The following two structs are for experimental purposes. See - * experimental_utilization_query_ctl and + * The following struct is for experimental purposes. See * experimental_utilization_batch_query_ctl in src/ctl.c. */ typedef struct inspect_extent_util_stats_s inspect_extent_util_stats_t; @@ -22,22 +21,7 @@ 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/src/ctl.c b/src/ctl.c index c0a602fc..727b63a9 100644 --- a/src/ctl.c +++ b/src/ctl.c @@ -372,7 +372,6 @@ 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_utilization_query) CTL_PROTO(experimental_utilization_batch_query) CTL_PROTO(experimental_prof_recent_alloc_max) CTL_PROTO(experimental_prof_recent_alloc_dump) @@ -911,7 +910,6 @@ static const ctl_named_node_t experimental_hooks_node[] = { }; 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_prof_recent_node[] = { @@ -4212,102 +4210,6 @@ 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: diff --git a/src/inspect.c b/src/inspect.c index 1c0de129..f46bc9ef 100644 --- a/src/inspect.c +++ b/src/inspect.c @@ -24,54 +24,3 @@ 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/test/unit/inspect.c b/test/unit/inspect.c index 8111e4a5..cb1cd979 100644 --- a/test/unit/inspect.c +++ b/test/unit/inspect.c @@ -11,8 +11,6 @@ "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) @@ -30,139 +28,6 @@ #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; /* @@ -217,10 +82,7 @@ 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"); - /* - * See the corresponding comment in test_query; profiling breaks - * our slab count expectations. - */ + /* 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"); @@ -270,5 +132,5 @@ int main(void) { assert_zu_lt(SC_SMALL_MAXCLASS + 100000, TEST_MAX_SIZE, "Test case cannot cover large classes"); - return test(test_query, test_batch); + return test(test_batch); } From 2004cf039e7096a7a5f2f436565b5ee88a074bd5 Mon Sep 17 00:00:00 2001 From: Slobodan Predolac Date: Thu, 28 May 2026 18:02:59 -0700 Subject: [PATCH 8/8] Speculative fix for Windows tasks that sometime fail on aligned_alloc --- test/test.sh.in | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/test/test.sh.in b/test/test.sh.in index dc13bc28..8eca840a 100644 --- a/test/test.sh.in +++ b/test/test.sh.in @@ -2,15 +2,30 @@ case @abi@ in macho) - export DYLD_FALLBACK_LIBRARY_PATH="@objroot@lib" + export DYLD_FALLBACK_LIBRARY_PATH="@abs_objroot@lib" ;; pecoff) - export PATH="${PATH}:@objroot@lib" + export PATH="@abs_objroot@lib:${PATH}" ;; *) ;; 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} @@ -45,10 +60,12 @@ 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