Add stats for stashed bytes in tcache.

This commit is contained in:
Qi Wang
2021-11-30 14:39:34 -08:00
committed by Qi Wang
parent b75822bc6e
commit e491cef9ab
9 changed files with 112 additions and 36 deletions

View File

@@ -266,7 +266,8 @@ do_flush_stashed_test(cache_bin_t *bin, cache_bin_info_t *info, void **ptrs,
cache_bin_sz_t nfill, cache_bin_sz_t nstash) {
expect_true(cache_bin_ncached_get_local(bin, info) == 0,
"Bin not empty");
expect_true(cache_bin_nstashed_get(bin, info) == 0, "Bin not empty");
expect_true(cache_bin_nstashed_get_local(bin, info) == 0,
"Bin not empty");
expect_true(nfill + nstash <= info->ncached_max, "Exceeded max");
bool ret;
@@ -283,7 +284,7 @@ do_flush_stashed_test(cache_bin_t *bin, cache_bin_info_t *info, void **ptrs,
ret = cache_bin_stash(bin, &ptrs[i + nfill]);
expect_true(ret, "Unexpected stash failure");
}
expect_true(cache_bin_nstashed_get(bin, info) == nstash,
expect_true(cache_bin_nstashed_get_local(bin, info) == nstash,
"Wrong stashed count");
if (nfill + nstash == info->ncached_max) {
@@ -303,7 +304,7 @@ do_flush_stashed_test(cache_bin_t *bin, cache_bin_info_t *info, void **ptrs,
}
expect_true(cache_bin_ncached_get_local(bin, info) == 0,
"Wrong cached count");
expect_true(cache_bin_nstashed_get(bin, info) == nstash,
expect_true(cache_bin_nstashed_get_local(bin, info) == nstash,
"Wrong stashed count");
cache_bin_alloc(bin, &ret);
@@ -313,7 +314,7 @@ do_flush_stashed_test(cache_bin_t *bin, cache_bin_info_t *info, void **ptrs,
cache_bin_finish_flush_stashed(bin, info);
expect_true(cache_bin_ncached_get_local(bin, info) == 0,
"Wrong cached count");
expect_true(cache_bin_nstashed_get(bin, info) == 0,
expect_true(cache_bin_nstashed_get_local(bin, info) == 0,
"Wrong stashed count");
cache_bin_alloc(bin, &ret);
@@ -338,7 +339,7 @@ TEST_BEGIN(test_cache_bin_stash) {
for (cache_bin_sz_t i = 0; i < ncached_max; i++) {
expect_true(cache_bin_ncached_get_local(&bin, &info) ==
(i / 2 + i % 2), "Wrong ncached value");
expect_true(cache_bin_nstashed_get(&bin, &info) == i / 2,
expect_true(cache_bin_nstashed_get_local(&bin, &info) == i / 2,
"Wrong nstashed value");
if (i % 2 == 0) {
cache_bin_dalloc_easy(&bin, &ptrs[i]);
@@ -361,7 +362,7 @@ TEST_BEGIN(test_cache_bin_stash) {
expect_true(diff % 2 == 0, "Should be able to alloc");
} else {
expect_false(ret, "Should not alloc stashed");
expect_true(cache_bin_nstashed_get(&bin, &info) ==
expect_true(cache_bin_nstashed_get_local(&bin, &info) ==
ncached_max / 2, "Wrong nstashed value");
}
}

View File

@@ -367,7 +367,7 @@ TEST_END
static void
test_tcache_bytes_for_usize(size_t usize) {
uint64_t epoch;
size_t tcache_bytes;
size_t tcache_bytes, tcache_stashed_bytes;
size_t sz = sizeof(tcache_bytes);
void *ptr = mallocx(usize, 0);
@@ -377,7 +377,11 @@ test_tcache_bytes_for_usize(size_t usize) {
assert_d_eq(mallctl(
"stats.arenas." STRINGIFY(MALLCTL_ARENAS_ALL) ".tcache_bytes",
&tcache_bytes, &sz, NULL, 0), 0, "Unexpected mallctl failure");
size_t tcache_bytes_before = tcache_bytes;
assert_d_eq(mallctl(
"stats.arenas." STRINGIFY(MALLCTL_ARENAS_ALL)
".tcache_stashed_bytes", &tcache_stashed_bytes, &sz, NULL, 0), 0,
"Unexpected mallctl failure");
size_t tcache_bytes_before = tcache_bytes + tcache_stashed_bytes;
dallocx(ptr, 0);
expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
@@ -385,7 +389,11 @@ test_tcache_bytes_for_usize(size_t usize) {
assert_d_eq(mallctl(
"stats.arenas." STRINGIFY(MALLCTL_ARENAS_ALL) ".tcache_bytes",
&tcache_bytes, &sz, NULL, 0), 0, "Unexpected mallctl failure");
size_t tcache_bytes_after = tcache_bytes;
assert_d_eq(mallctl(
"stats.arenas." STRINGIFY(MALLCTL_ARENAS_ALL)
".tcache_stashed_bytes", &tcache_stashed_bytes, &sz, NULL, 0), 0,
"Unexpected mallctl failure");
size_t tcache_bytes_after = tcache_bytes + tcache_stashed_bytes;
assert_zu_eq(tcache_bytes_after - tcache_bytes_before,
usize, "Incorrectly attributed a free");
}

View File

@@ -53,6 +53,26 @@ uaf_detection_enabled(void) {
return true;
}
static size_t
read_tcache_stashed_bytes(unsigned arena_ind) {
if (!config_stats) {
return 0;
}
uint64_t epoch;
assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
0, "Unexpected mallctl() failure");
size_t tcache_stashed_bytes;
size_t sz = sizeof(tcache_stashed_bytes);
assert_d_eq(mallctl(
"stats.arenas." STRINGIFY(MALLCTL_ARENAS_ALL)
".tcache_stashed_bytes", &tcache_stashed_bytes, &sz, NULL, 0), 0,
"Unexpected mallctl failure");
return tcache_stashed_bytes;
}
static void
test_use_after_free(size_t alloc_size, bool write_after_free) {
void *ptr = (void *)(uintptr_t)san_uaf_align;
@@ -95,6 +115,7 @@ test_use_after_free(size_t alloc_size, bool write_after_free) {
while (iter-- != 0) {
char *volatile mem = items[iter];
assert_c_eq(*mem, magic, "Unexpected memory content");
size_t stashed_before = read_tcache_stashed_bytes(arena_ind);
free(mem);
if (*mem != magic) {
junked = true;
@@ -103,6 +124,18 @@ test_use_after_free(size_t alloc_size, bool write_after_free) {
if (write_after_free) {
*(char *)mem = magic + 1;
}
size_t stashed_after = read_tcache_stashed_bytes(
arena_ind);
/*
* An edge case is the deallocation above triggering the
* tcache GC event, in which case the stashed pointers
* may get flushed immediately, before returning from
* free(). Treat these cases as checked already.
*/
if (stashed_after <= stashed_before) {
fake_abort_called = true;
}
}
/* Flush tcache (including stashed). */
assert_d_eq(mallctl("thread.tcache.flush", NULL, NULL, NULL, 0),