diff --git a/include/jemalloc/internal/jemalloc_internal_externs.h b/include/jemalloc/internal/jemalloc_internal_externs.h index 64d9aa20..9d7a9048 100644 --- a/include/jemalloc/internal/jemalloc_internal_externs.h +++ b/include/jemalloc/internal/jemalloc_internal_externs.h @@ -38,6 +38,9 @@ extern atomic_zu_t zero_realloc_count; extern bool opt_cache_oblivious; extern unsigned opt_debug_double_free_max_scan; +extern const char *opt_malloc_conf_symlink; +extern const char *opt_malloc_conf_env_var; + /* 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 93144752..7c349da7 100644 --- a/src/ctl.c +++ b/src/ctl.c @@ -159,6 +159,10 @@ CTL_PROTO(opt_prof_sys_thread_name) CTL_PROTO(opt_prof_time_res) CTL_PROTO(opt_lg_san_uaf_align) CTL_PROTO(opt_zero_realloc) +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) @@ -426,6 +430,14 @@ static const ctl_named_node_t config_node[] = { {NAME("xmalloc"), CTL(config_xmalloc)} }; +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)} +}; + static const ctl_named_node_t opt_node[] = { {NAME("abort"), CTL(opt_abort)}, {NAME("abort_conf"), CTL(opt_abort_conf)}, @@ -502,7 +514,8 @@ static const ctl_named_node_t opt_node[] = { {NAME("lg_san_uaf_align"), CTL(opt_lg_san_uaf_align)}, {NAME("zero_realloc"), CTL(opt_zero_realloc)}, {NAME("debug_double_free_max_scan"), - CTL(opt_debug_double_free_max_scan)} + CTL(opt_debug_double_free_max_scan)}, + {NAME("malloc_conf"), CHILD(named, opt_malloc_conf)} }; static const ctl_named_node_t tcache_node[] = { @@ -2230,6 +2243,17 @@ CTL_RO_NL_CGEN(config_uaf_detection, opt_lg_san_uaf_align, CTL_RO_NL_GEN(opt_zero_realloc, zero_realloc_mode_names[opt_zero_realloc_action], const char *) +/* malloc_conf options */ +CTL_RO_NL_CGEN(opt_malloc_conf_symlink, opt_malloc_conf_symlink, + opt_malloc_conf_symlink, const char *) +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 *) + /******************************************************************************/ static int diff --git a/src/jemalloc.c b/src/jemalloc.c index 7934e767..68c0e7eb 100644 --- a/src/jemalloc.c +++ b/src/jemalloc.c @@ -57,6 +57,9 @@ const char *je_malloc_conf_2_conf_harder #endif ; +const char *opt_malloc_conf_symlink = NULL; +const char *opt_malloc_conf_env_var = NULL; + bool opt_abort = #ifdef JEMALLOC_DEBUG true @@ -955,7 +958,7 @@ malloc_slow_flag_init(void) { #define MALLOC_CONF_NSOURCES 5 static const char * -obtain_malloc_conf(unsigned which_source, char buf[PATH_MAX + 1]) { +obtain_malloc_conf(unsigned which_source, char readlink_buf[PATH_MAX + 1]) { if (config_debug) { static unsigned read_source = 0; /* @@ -998,9 +1001,9 @@ obtain_malloc_conf(unsigned which_source, char buf[PATH_MAX + 1]) { * link's name. */ #ifndef JEMALLOC_READLINKAT - linklen = readlink(linkname, buf, PATH_MAX); + linklen = readlink(linkname, readlink_buf, PATH_MAX); #else - linklen = readlinkat(AT_FDCWD, linkname, buf, PATH_MAX); + linklen = readlinkat(AT_FDCWD, linkname, readlink_buf, PATH_MAX); #endif if (linklen == -1) { /* No configuration specified. */ @@ -1009,8 +1012,8 @@ obtain_malloc_conf(unsigned which_source, char buf[PATH_MAX + 1]) { set_errno(saved_errno); } #endif - buf[linklen] = '\0'; - ret = buf; + readlink_buf[linklen] = '\0'; + ret = readlink_buf; break; } case 3: { const char *envname = @@ -1022,10 +1025,7 @@ obtain_malloc_conf(unsigned which_source, char buf[PATH_MAX + 1]) { ; if ((ret = jemalloc_getenv(envname)) != NULL) { - /* - * Do nothing; opts is already initialized to the value - * of the MALLOC_CONF environment variable. - */ + opt_malloc_conf_env_var = ret; } else { /* No configuration specified. */ ret = NULL; @@ -1084,7 +1084,7 @@ validate_hpa_settings(void) { static void malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS], bool initial_call, const char *opts_cache[MALLOC_CONF_NSOURCES], - char buf[PATH_MAX + 1]) { + char readlink_buf[PATH_MAX + 1]) { static const char *opts_explain[MALLOC_CONF_NSOURCES] = { "string specified via --with-malloc-conf", "string pointed to by the global variable malloc_conf", @@ -1101,7 +1101,7 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS], for (i = 0; i < MALLOC_CONF_NSOURCES; i++) { /* Get runtime configuration. */ if (initial_call) { - opts_cache[i] = obtain_malloc_conf(i, buf); + opts_cache[i] = obtain_malloc_conf(i, readlink_buf); } opts = opts_cache[i]; if (!initial_call && opt_confirm_conf) { @@ -1783,13 +1783,13 @@ malloc_conf_init_check_deps(void) { } static void -malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) { +malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS], + char readlink_buf[PATH_MAX + 1]) { const char *opts_cache[MALLOC_CONF_NSOURCES] = {NULL, NULL, NULL, NULL, NULL}; - char buf[PATH_MAX + 1]; /* The first call only set the confirm_conf option and opts_cache */ - malloc_conf_init_helper(NULL, NULL, true, opts_cache, buf); + malloc_conf_init_helper(NULL, NULL, true, opts_cache, readlink_buf); malloc_conf_init_helper(sc_data, bin_shard_sizes, false, opts_cache, NULL); if (malloc_conf_init_check_deps()) { @@ -1855,7 +1855,9 @@ malloc_init_hard_a0_locked(void) { if (config_prof) { prof_boot0(); } - malloc_conf_init(&sc_data, bin_shard_sizes); + char readlink_buf[PATH_MAX + 1]; + readlink_buf[0] = '\0'; + malloc_conf_init(&sc_data, bin_shard_sizes, readlink_buf); san_init(opt_lg_san_uaf_align); sz_boot(&sc_data, opt_cache_oblivious); bin_info_boot(&sc_data, bin_shard_sizes); @@ -1949,6 +1951,15 @@ malloc_init_hard_a0_locked(void) { malloc_init_state = malloc_init_a0_initialized; + size_t buf_len = strlen(readlink_buf); + if (buf_len > 0) { + void *readlink_allocated = a0ialloc(buf_len + 1, false, true); + if (readlink_allocated != NULL) { + memcpy(readlink_allocated, readlink_buf, buf_len + 1); + opt_malloc_conf_symlink = readlink_allocated; + } + } + return false; } diff --git a/src/stats.c b/src/stats.c index c580b49e..428e8ffb 100644 --- a/src/stats.c +++ b/src/stats.c @@ -1473,6 +1473,40 @@ stats_general_print(emitter_t *emitter) { emitter_dict_begin(emitter, "opt", "Run-time option settings"); + /* + * opt.malloc_conf. + * + * Sources are documented in https://jemalloc.net/jemalloc.3.html#tuning + * - (Not Included Here) The string specified via --with-malloc-conf, + * which is already printed out above as config.malloc_conf + * - (Included) The string pointed to by the global variable malloc_conf + * - (Included) The “name” of the file referenced by the symbolic link + * named /etc/malloc.conf + * - (Included) The value of the environment variable MALLOC_CONF + * - (Optional, Unofficial) The string pointed to by the global variable + * malloc_conf_2_conf_harder, which is hidden from the public. + * + * Note: The outputs are strictly ordered by priorities (low -> high). + * + */ +#define MALLOC_CONF_WRITE(name, message) \ + if (je_mallctl("opt.malloc_conf."name, (void *)&cpv, &cpsz, NULL, 0) != \ + 0) { \ + cpv = ""; \ + } \ + emitter_kv(emitter, name, message, emitter_type_string, &cpv); + + 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) { + emitter_kv(emitter, "global_var_2_conf_harder", "Global " + "variable malloc_conf_2_conf_harder", emitter_type_string, &cpv); + } +#undef MALLOC_CONF_WRITE + OPT_WRITE_BOOL("abort") OPT_WRITE_BOOL("abort_conf") OPT_WRITE_BOOL("cache_oblivious") @@ -1554,7 +1588,7 @@ stats_general_print(emitter_t *emitter) { OPT_WRITE_CHAR_P("stats_interval_opts") OPT_WRITE_CHAR_P("zero_realloc") - emitter_dict_end(emitter); + emitter_dict_end(emitter); /* Close "opt". */ #undef OPT_WRITE #undef OPT_WRITE_MUTABLE diff --git a/test/unit/malloc_conf_2.c b/test/unit/malloc_conf_2.c index ecfa4991..9d2c6077 100644 --- a/test/unit/malloc_conf_2.c +++ b/test/unit/malloc_conf_2.c @@ -22,8 +22,32 @@ TEST_BEGIN(test_malloc_conf_2) { } TEST_END +TEST_BEGIN(test_mallctl_global_var) { +#ifdef _WIN32 + bool windows = true; +#else + bool windows = false; +#endif + /* Windows doesn't support weak symbol linker trickery. */ + test_skip_if(windows); + + const char *mc; + size_t sz = sizeof(mc); + expect_d_eq(mallctl("opt.malloc_conf.global_var", + (void *)&mc, &sz, NULL, 0), 0, "Unexpected mallctl() failure"); + 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 + int main(void) { return test( - test_malloc_conf_2); + test_malloc_conf_2, + test_mallctl_global_var); }