34b00f8969
With prof enabled, number of page aligned allocations doesn't match the number of slab "ends" because prof allocations skew the addresses. It leads to 'pages' array overflow and hard to debug failures.
206 lines
5.8 KiB
C
206 lines
5.8 KiB
C
#include "test/jemalloc_test.h"
|
|
#include "test/arena_decay.h"
|
|
#include "test/san.h"
|
|
|
|
#include "jemalloc/internal/san.h"
|
|
|
|
static void
|
|
verify_extent_guarded(tsdn_t *tsdn, void *ptr) {
|
|
expect_true(extent_is_guarded(tsdn, ptr),
|
|
"All extents should be guarded.");
|
|
}
|
|
|
|
#define MAX_SMALL_ALLOCATIONS 4096
|
|
void *small_alloc[MAX_SMALL_ALLOCATIONS];
|
|
|
|
TEST_BEGIN(test_guarded_small) {
|
|
test_skip_if(opt_prof);
|
|
|
|
tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
|
|
unsigned npages = 16, pages_found = 0, ends_found = 0;
|
|
VARIABLE_ARRAY(uintptr_t, pages, npages);
|
|
|
|
/* Allocate to get sanitized pointers. */
|
|
size_t sz = PAGE / 8;
|
|
unsigned n_alloc = 0;
|
|
while (n_alloc < MAX_SMALL_ALLOCATIONS) {
|
|
void *ptr = malloc(sz);
|
|
expect_ptr_not_null(ptr, "Unexpected malloc() failure");
|
|
small_alloc[n_alloc] = ptr;
|
|
verify_extent_guarded(tsdn, ptr);
|
|
if ((uintptr_t)ptr % PAGE == 0) {
|
|
assert_u_lt(pages_found, npages,
|
|
"Unexpectedly large number of page aligned allocs");
|
|
pages[pages_found++] = (uintptr_t)ptr;
|
|
}
|
|
if (((uintptr_t)ptr + (uintptr_t)sz) % PAGE == 0) {
|
|
ends_found++;
|
|
}
|
|
n_alloc++;
|
|
if (pages_found == npages && ends_found == npages) {
|
|
break;
|
|
}
|
|
}
|
|
/* Should found the ptrs being checked for overflow and underflow. */
|
|
expect_u_eq(pages_found, npages, "Could not found the expected pages.");
|
|
expect_u_eq(ends_found, npages, "Could not found the expected pages.");
|
|
|
|
/* Verify the pages are not continuous, i.e. separated by guards. */
|
|
for (unsigned i = 0; i < npages - 1; i++) {
|
|
for (unsigned j = i + 1; j < npages; j++) {
|
|
uintptr_t ptr_diff = pages[i] > pages[j] ?
|
|
pages[i] - pages[j] : pages[j] - pages[i];
|
|
expect_zu_gt((size_t)ptr_diff, 2 * PAGE,
|
|
"Pages should not be next to each other.");
|
|
}
|
|
}
|
|
|
|
for (unsigned i = 0; i < n_alloc + 1; i++) {
|
|
free(small_alloc[i]);
|
|
}
|
|
}
|
|
TEST_END
|
|
|
|
TEST_BEGIN(test_guarded_large) {
|
|
tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
|
|
unsigned nlarge = 32;
|
|
VARIABLE_ARRAY(uintptr_t, large, nlarge);
|
|
|
|
/* Allocate to get sanitized pointers. */
|
|
size_t large_sz = SC_LARGE_MINCLASS;
|
|
for (unsigned i = 0; i < nlarge; i++) {
|
|
void *ptr = malloc(large_sz);
|
|
verify_extent_guarded(tsdn, ptr);
|
|
expect_ptr_not_null(ptr, "Unexpected malloc() failure");
|
|
large[i] = (uintptr_t)ptr;
|
|
}
|
|
|
|
/* Verify the pages are not continuous, i.e. separated by guards. */
|
|
uintptr_t min_diff = (uintptr_t)-1;
|
|
for (unsigned i = 0; i < nlarge; i++) {
|
|
for (unsigned j = i + 1; j < nlarge; j++) {
|
|
uintptr_t ptr_diff = large[i] > large[j] ?
|
|
large[i] - large[j] : large[j] - large[i];
|
|
expect_zu_ge((size_t)ptr_diff, large_sz + 2 * PAGE,
|
|
"Pages should not be next to each other.");
|
|
if (ptr_diff < min_diff) {
|
|
min_diff = ptr_diff;
|
|
}
|
|
}
|
|
}
|
|
expect_zu_ge((size_t)min_diff, large_sz + 2 * PAGE,
|
|
"Pages should not be next to each other.");
|
|
|
|
for (unsigned i = 0; i < nlarge; i++) {
|
|
free((void *)large[i]);
|
|
}
|
|
}
|
|
TEST_END
|
|
|
|
static void
|
|
verify_pdirty(unsigned arena_ind, uint64_t expected) {
|
|
uint64_t pdirty = get_arena_pdirty(arena_ind);
|
|
expect_u64_eq(pdirty, expected / PAGE,
|
|
"Unexpected dirty page amount.");
|
|
}
|
|
|
|
static void
|
|
verify_pmuzzy(unsigned arena_ind, uint64_t expected) {
|
|
uint64_t pmuzzy = get_arena_pmuzzy(arena_ind);
|
|
expect_u64_eq(pmuzzy, expected / PAGE,
|
|
"Unexpected muzzy page amount.");
|
|
}
|
|
|
|
TEST_BEGIN(test_guarded_decay) {
|
|
unsigned arena_ind = do_arena_create(-1, -1);
|
|
do_decay(arena_ind);
|
|
do_purge(arena_ind);
|
|
|
|
verify_pdirty(arena_ind, 0);
|
|
verify_pmuzzy(arena_ind, 0);
|
|
|
|
/* Verify that guarded extents as dirty. */
|
|
size_t sz1 = PAGE, sz2 = PAGE * 2;
|
|
/* W/o maps_coalesce, guarded extents are unguarded eagerly. */
|
|
size_t add_guard_size = maps_coalesce ? 0 : PAGE_GUARDS_SIZE;
|
|
generate_dirty(arena_ind, sz1);
|
|
verify_pdirty(arena_ind, sz1 + add_guard_size);
|
|
verify_pmuzzy(arena_ind, 0);
|
|
|
|
/* Should reuse the first extent. */
|
|
generate_dirty(arena_ind, sz1);
|
|
verify_pdirty(arena_ind, sz1 + add_guard_size);
|
|
verify_pmuzzy(arena_ind, 0);
|
|
|
|
/* Should not reuse; expect new dirty pages. */
|
|
generate_dirty(arena_ind, sz2);
|
|
verify_pdirty(arena_ind, sz1 + sz2 + 2 * add_guard_size);
|
|
verify_pmuzzy(arena_ind, 0);
|
|
|
|
tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
|
|
int flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;
|
|
|
|
/* Should reuse dirty extents for the two mallocx. */
|
|
void *p1 = do_mallocx(sz1, flags);
|
|
verify_extent_guarded(tsdn, p1);
|
|
verify_pdirty(arena_ind, sz2 + add_guard_size);
|
|
|
|
void *p2 = do_mallocx(sz2, flags);
|
|
verify_extent_guarded(tsdn, p2);
|
|
verify_pdirty(arena_ind, 0);
|
|
verify_pmuzzy(arena_ind, 0);
|
|
|
|
dallocx(p1, flags);
|
|
verify_pdirty(arena_ind, sz1 + add_guard_size);
|
|
dallocx(p2, flags);
|
|
verify_pdirty(arena_ind, sz1 + sz2 + 2 * add_guard_size);
|
|
verify_pmuzzy(arena_ind, 0);
|
|
|
|
do_purge(arena_ind);
|
|
verify_pdirty(arena_ind, 0);
|
|
verify_pmuzzy(arena_ind, 0);
|
|
|
|
if (config_stats) {
|
|
expect_u64_eq(get_arena_npurge(arena_ind), 1,
|
|
"Expected purging to occur");
|
|
expect_u64_eq(get_arena_dirty_npurge(arena_ind), 1,
|
|
"Expected purging to occur");
|
|
expect_u64_eq(get_arena_dirty_purged(arena_ind),
|
|
(sz1 + sz2 + 2 * add_guard_size) / PAGE,
|
|
"Expected purging to occur");
|
|
expect_u64_eq(get_arena_muzzy_npurge(arena_ind), 0,
|
|
"Expected purging to occur");
|
|
}
|
|
|
|
if (opt_retain) {
|
|
/*
|
|
* With retain, guarded extents are not mergable and will be
|
|
* cached in ecache_retained. They should be reused.
|
|
*/
|
|
void *new_p1 = do_mallocx(sz1, flags);
|
|
verify_extent_guarded(tsdn, p1);
|
|
expect_ptr_eq(p1, new_p1, "Expect to reuse p1");
|
|
|
|
void *new_p2 = do_mallocx(sz2, flags);
|
|
verify_extent_guarded(tsdn, p2);
|
|
expect_ptr_eq(p2, new_p2, "Expect to reuse p2");
|
|
|
|
dallocx(new_p1, flags);
|
|
verify_pdirty(arena_ind, sz1 + add_guard_size);
|
|
dallocx(new_p2, flags);
|
|
verify_pdirty(arena_ind, sz1 + sz2 + 2 * add_guard_size);
|
|
verify_pmuzzy(arena_ind, 0);
|
|
}
|
|
|
|
do_arena_destroy(arena_ind);
|
|
}
|
|
TEST_END
|
|
|
|
int
|
|
main(void) {
|
|
return test(
|
|
test_guarded_small,
|
|
test_guarded_large,
|
|
test_guarded_decay);
|
|
}
|