Add double free detection in thread cache for debug build
Add new runtime option `debug_double_free_max_scan` that specifies the max number of stack entries to scan in the cache bit when trying to detect the double free bug (currently debug build only).
This commit is contained in:
@@ -10,13 +10,13 @@ void fake_abort(const char *message) {
|
||||
}
|
||||
|
||||
void
|
||||
test_large_double_free_pre(void) {
|
||||
test_double_free_pre(void) {
|
||||
safety_check_set_abort(&fake_abort);
|
||||
fake_abort_called = false;
|
||||
}
|
||||
|
||||
void
|
||||
test_large_double_free_post() {
|
||||
test_double_free_post() {
|
||||
expect_b_eq(fake_abort_called, true, "Double-free check didn't fire.");
|
||||
safety_check_set_abort(NULL);
|
||||
}
|
||||
@@ -29,7 +29,7 @@ TEST_BEGIN(test_large_double_free_tcache) {
|
||||
*/
|
||||
test_skip_if(config_debug);
|
||||
|
||||
test_large_double_free_pre();
|
||||
test_double_free_pre();
|
||||
char *ptr = malloc(SC_LARGE_MINCLASS);
|
||||
bool guarded = extent_is_guarded(tsdn_fetch(), ptr);
|
||||
free(ptr);
|
||||
@@ -44,7 +44,7 @@ TEST_BEGIN(test_large_double_free_tcache) {
|
||||
fake_abort_called = true;
|
||||
}
|
||||
mallctl("thread.tcache.flush", NULL, NULL, NULL, 0);
|
||||
test_large_double_free_post();
|
||||
test_double_free_post();
|
||||
}
|
||||
TEST_END
|
||||
|
||||
@@ -52,7 +52,7 @@ TEST_BEGIN(test_large_double_free_no_tcache) {
|
||||
test_skip_if(!config_opt_safety_checks);
|
||||
test_skip_if(config_debug);
|
||||
|
||||
test_large_double_free_pre();
|
||||
test_double_free_pre();
|
||||
char *ptr = mallocx(SC_LARGE_MINCLASS, MALLOCX_TCACHE_NONE);
|
||||
bool guarded = extent_is_guarded(tsdn_fetch(), ptr);
|
||||
dallocx(ptr, MALLOCX_TCACHE_NONE);
|
||||
@@ -66,12 +66,45 @@ TEST_BEGIN(test_large_double_free_no_tcache) {
|
||||
*/
|
||||
fake_abort_called = true;
|
||||
}
|
||||
test_large_double_free_post();
|
||||
test_double_free_post();
|
||||
}
|
||||
TEST_END
|
||||
|
||||
TEST_BEGIN(test_small_double_free_tcache) {
|
||||
test_skip_if(!config_debug);
|
||||
|
||||
test_skip_if(opt_debug_double_free_max_scan == 0);
|
||||
|
||||
bool tcache_enabled;
|
||||
size_t sz = sizeof(tcache_enabled);
|
||||
assert_d_eq(
|
||||
mallctl("thread.tcache.enabled", &tcache_enabled, &sz, NULL, 0), 0,
|
||||
"Unexpected mallctl failure");
|
||||
test_skip_if(!tcache_enabled);
|
||||
|
||||
test_double_free_pre();
|
||||
char *ptr = malloc(1);
|
||||
bool guarded = extent_is_guarded(tsdn_fetch(), ptr);
|
||||
free(ptr);
|
||||
if (!guarded) {
|
||||
free(ptr);
|
||||
} else {
|
||||
/*
|
||||
* Skip because guarded extents may unguard immediately on
|
||||
* deallocation, in which case the second free will crash before
|
||||
* reaching the intended safety check.
|
||||
*/
|
||||
fake_abort_called = true;
|
||||
}
|
||||
mallctl("thread.tcache.flush", NULL, NULL, NULL, 0);
|
||||
test_double_free_post();
|
||||
}
|
||||
TEST_END
|
||||
|
||||
int
|
||||
main(void) {
|
||||
return test(test_large_double_free_no_tcache,
|
||||
test_large_double_free_tcache);
|
||||
return test(
|
||||
test_large_double_free_no_tcache,
|
||||
test_large_double_free_tcache,
|
||||
test_small_double_free_tcache);
|
||||
}
|
||||
|
@@ -325,6 +325,7 @@ TEST_BEGIN(test_mallctl_opt) {
|
||||
TEST_MALLCTL_OPT(bool, prof_stats, prof);
|
||||
TEST_MALLCTL_OPT(bool, prof_sys_thread_name, prof);
|
||||
TEST_MALLCTL_OPT(ssize_t, lg_san_uaf_align, uaf_detection);
|
||||
TEST_MALLCTL_OPT(unsigned, debug_double_free_max_scan, always);
|
||||
|
||||
#undef TEST_MALLCTL_OPT
|
||||
}
|
||||
|
Reference in New Issue
Block a user