Realloc: Make behavior of realloc(ptr, 0) configurable.
This commit is contained in:
committed by
David Goldblatt
parent
ee961c2310
commit
9cfa805947
@@ -112,6 +112,7 @@ CTL_PROTO(opt_prof_gdump)
|
||||
CTL_PROTO(opt_prof_final)
|
||||
CTL_PROTO(opt_prof_leak)
|
||||
CTL_PROTO(opt_prof_accum)
|
||||
CTL_PROTO(opt_zero_realloc)
|
||||
CTL_PROTO(tcache_create)
|
||||
CTL_PROTO(tcache_flush)
|
||||
CTL_PROTO(tcache_destroy)
|
||||
@@ -339,7 +340,8 @@ static const ctl_named_node_t opt_node[] = {
|
||||
{NAME("prof_gdump"), CTL(opt_prof_gdump)},
|
||||
{NAME("prof_final"), CTL(opt_prof_final)},
|
||||
{NAME("prof_leak"), CTL(opt_prof_leak)},
|
||||
{NAME("prof_accum"), CTL(opt_prof_accum)}
|
||||
{NAME("prof_accum"), CTL(opt_prof_accum)},
|
||||
{NAME("zero_realloc"), CTL(opt_zero_realloc)}
|
||||
};
|
||||
|
||||
static const ctl_named_node_t tcache_node[] = {
|
||||
@@ -1793,6 +1795,8 @@ CTL_RO_NL_CGEN(config_prof, opt_lg_prof_interval, opt_lg_prof_interval, ssize_t)
|
||||
CTL_RO_NL_CGEN(config_prof, opt_prof_gdump, opt_prof_gdump, bool)
|
||||
CTL_RO_NL_CGEN(config_prof, opt_prof_final, opt_prof_final, bool)
|
||||
CTL_RO_NL_CGEN(config_prof, opt_prof_leak, opt_prof_leak, bool)
|
||||
CTL_RO_NL_GEN(opt_zero_realloc,
|
||||
zero_realloc_mode_names[opt_zero_realloc_action], const char *)
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
@@ -67,6 +67,15 @@ bool opt_junk_free =
|
||||
#endif
|
||||
;
|
||||
|
||||
zero_realloc_action_t opt_zero_realloc_action =
|
||||
zero_realloc_action_strict;
|
||||
|
||||
const char *zero_realloc_mode_names[] = {
|
||||
"strict",
|
||||
"free",
|
||||
"abort",
|
||||
};
|
||||
|
||||
bool opt_utrace = false;
|
||||
bool opt_xmalloc = false;
|
||||
bool opt_zero = false;
|
||||
@@ -1411,6 +1420,22 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
|
||||
}
|
||||
CONF_CONTINUE;
|
||||
}
|
||||
if (CONF_MATCH("zero_realloc")) {
|
||||
if (CONF_MATCH_VALUE("strict")) {
|
||||
opt_zero_realloc_action
|
||||
= zero_realloc_action_strict;
|
||||
} else if (CONF_MATCH_VALUE("free")) {
|
||||
opt_zero_realloc_action
|
||||
= zero_realloc_action_free;
|
||||
} else if (CONF_MATCH_VALUE("abort")) {
|
||||
opt_zero_realloc_action
|
||||
= zero_realloc_action_abort;
|
||||
} else {
|
||||
CONF_ERROR("Invalid conf value",
|
||||
k, klen, v, vlen);
|
||||
}
|
||||
CONF_CONTINUE;
|
||||
}
|
||||
CONF_ERROR("Invalid conf pair", k, klen, v, vlen);
|
||||
#undef CONF_ERROR
|
||||
#undef CONF_CONTINUE
|
||||
@@ -3133,18 +3158,17 @@ je_rallocx(void *ptr, size_t size, int flags) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
|
||||
void JEMALLOC_NOTHROW *
|
||||
JEMALLOC_ALLOC_SIZE(2)
|
||||
je_realloc(void *ptr, size_t size) {
|
||||
LOG("core.realloc.entry", "ptr: %p, size: %zu\n", ptr, size);
|
||||
|
||||
if (likely(ptr != NULL && size != 0)) {
|
||||
void *ret = do_rallocx(ptr, size, 0, true);
|
||||
LOG("core.realloc.exit", "result: %p", ret);
|
||||
return ret;
|
||||
} else if (ptr != NULL && size == 0) {
|
||||
/* realloc(ptr, 0) is equivalent to free(ptr). */
|
||||
static void *
|
||||
do_realloc_nonnull_zero(void *ptr) {
|
||||
if (opt_zero_realloc_action == zero_realloc_action_strict) {
|
||||
/*
|
||||
* The user might have gotten a strict setting while expecting a
|
||||
* free setting. If that's the case, we at least try to
|
||||
* reduce the harm, and turn off the tcache while allocating, so
|
||||
* that we'll get a true first fit.
|
||||
*/
|
||||
return do_rallocx(ptr, 1, MALLOCX_TCACHE_NONE, true);
|
||||
} else if (opt_zero_realloc_action == zero_realloc_action_free) {
|
||||
UTRACE(ptr, 0, 0);
|
||||
tcache_t *tcache;
|
||||
tsd_t *tsd = tsd_fetch();
|
||||
@@ -3156,14 +3180,39 @@ je_realloc(void *ptr, size_t size) {
|
||||
tcache = NULL;
|
||||
}
|
||||
|
||||
uintptr_t args[3] = {(uintptr_t)ptr, size};
|
||||
uintptr_t args[3] = {(uintptr_t)ptr, 0};
|
||||
hook_invoke_dalloc(hook_dalloc_realloc, ptr, args);
|
||||
|
||||
ifree(tsd, ptr, tcache, true);
|
||||
|
||||
check_entry_exit_locking(tsd_tsdn(tsd));
|
||||
LOG("core.realloc.exit", "result: %p", NULL);
|
||||
return NULL;
|
||||
} else {
|
||||
safety_check_fail("Called realloc(non-null-ptr, 0) with "
|
||||
"zero_realloc:abort set\n");
|
||||
/* In real code, this will never run; the safety check failure
|
||||
* will call abort. In the unit test, we just want to bail out
|
||||
* without corrupting internal state that the test needs to
|
||||
* finish.
|
||||
*/
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
|
||||
void JEMALLOC_NOTHROW *
|
||||
JEMALLOC_ALLOC_SIZE(2)
|
||||
je_realloc(void *ptr, size_t size) {
|
||||
LOG("core.realloc.entry", "ptr: %p, size: %zu\n", ptr, size);
|
||||
|
||||
if (likely(ptr != NULL && size != 0)) {
|
||||
void *ret = do_rallocx(ptr, size, 0, true);
|
||||
LOG("core.realloc.exit", "result: %p", ret);
|
||||
return ret;
|
||||
} else if (ptr != NULL && size == 0) {
|
||||
void *ret = do_realloc_nonnull_zero(ptr);
|
||||
LOG("core.realloc.exit", "result: %p", ret);
|
||||
return ret;
|
||||
} else {
|
||||
/* realloc(NULL, size) is equivalent to malloc(size). */
|
||||
void *ret;
|
||||
|
@@ -1109,6 +1109,7 @@ stats_general_print(emitter_t *emitter) {
|
||||
OPT_WRITE_BOOL("prof_leak")
|
||||
OPT_WRITE_BOOL("stats_print")
|
||||
OPT_WRITE_CHAR_P("stats_print_opts")
|
||||
OPT_WRITE_CHAR_P("zero_realloc")
|
||||
|
||||
emitter_dict_end(emitter);
|
||||
|
||||
|
Reference in New Issue
Block a user